This patch is sponsored by Acquia (as well as a fair amount of my own time) and is a first attempt at rewriting Drupal's installer to allow non-interactive, command line installations.

There are obviously a number of projects that want to be able to install Drupal without clicking through pages in a web browser:

  1. Aegir and Drush.
  2. PIFR, which runs the Drupal 7 testbot.
  3. Simpletest itself, which needs to install Drupal 7 before each test suite is run.
  4. I hear Acquia has a project in the works... :)

Currently, all of the above are forced to install Drupal via fragile, hacked-together methods. The goal of this patch is to make it so they have a simple, clean, robust way to do so. #1, #2, and #4 should be immediately achievable with this patch - #3 is more complicated since the installation happens within the context of an already-installed D7 site, but hopefully would be achievable down the road.

As a side-effect of the refactoring that needed to occur to write this patch, this also hopefully improves the experience of writing install profiles. Currently, the installer passes a string to install profiles and asks them to modify it in such a way that they "implement a state machine" if they want to run more than one custom task. (Huh?) After this patch, install profiles would simply define a structured array describing as many tasks as they want to run, and the installer automatically takes care of stepping through the tasks correctly, for either interactive (browser-based) or non-interactive (command line) installations.

Currently, the patch works (although I have not tested all edge cases), but there are two things to note:

  1. There are deliberate code style violations in this patch (indentation), in order to keep it from being totally unreviewable. Obviously those would be easy to fix later.
  2. For a similar reason, the patch currently leaves all the installer functionality in install.php, though we'd obviously want to follow up by moving all these functions into a separate includes/installer.inc kind of file, where command line scripts could actually access it. Therefore, to actually test a command line installation with this patch, you currently need to hack the bottom of install.php and replace install_drupal() with code similar to the following:
    $settings = array(
      'parameters' => array(
        'profile' => 'default',
        'locale' => 'en',
      ),
      'forms' => array(
        'install_settings_form' => array(
          'driver' => 'mysql',
          'database' => 'my_db_name',
          'username' => 'my_db_username',
          'password' => 'my_db_password',
        ),
        'install_configure_form' => array(
          'site_name' => 'My site',
          'site_mail' => 'admin@example.com',
          'account' => array(
            'name' => 'admin',
            'mail' => 'admin@example.com',
            'pass' => array(
              'pass1' => 'my_site_password',
              'pass2' => 'my_site_password',
            ),
          ),
          'update_status_module' => array(1 => TRUE),
          'clean_url' => TRUE,
        ),
      ),
    );
    install_drupal($settings);
    

    You can then run php install.php from the command line directly.

Comments

David_Rothstein’s picture

Status: Active » Needs review
StatusFileSize
new95.49 KB

Here is the patch.

Thanks to Joshua Rogers for helping to review and debug earlier versions of this. Just based on looking at the patch, he predicted a couple specific tests that this might break (at least the earlier version of the patch), so we'll see if he was right - I have not actually run the tests yet at all :)

Status: Needs review » Needs work

The last submitted patch failed testing.

David_Rothstein’s picture

Status: Needs work » Needs review

Well, that is a truly impressive number of failures - in fact, it may set the record :)

I promise that if you actually install Drupal with this patch and click around your site, it doesn't seem broken, so I guess we'll have to investigate to figure out what's going on.

David_Rothstein’s picture

Status: Needs review » Needs work

Did not mean to set it back to need review -- certainly no need to relive the experience of seeing "7236 passes, 5600 fails, 991 exceptions" appear on the screen :)

moshe weitzman’s picture

subscribe. thanks for your work here, david.

David_Rothstein’s picture

Status: Needs work » Needs review
StatusFileSize
new96.61 KB

Turns out Mr. Rogers was 100% correct. The hacked installation in DrupalWebTestCase just needed to be hacked in a different way as a result of this patch.

This should hopefully fix most (if not all) of the test failures.

SeanBannister’s picture

Sub, this is awesome.

cbrookins’s picture

Awesome work David

pwolanin’s picture

@David - currently we specifically block simpletest from using install.php for security reasons. However, it would seem to make sense to get most of the code out of install.php and into a .inc file or some such (e.g. the way xmlrpc.php has nearly no code in it).

adrian’s picture

We are using hacked together methods in aegir because we need to support d6 too.

we support making d7 easier to install from the command line.

Regarding tasks in .info files, I need to re-evaluate moving them back.

I believe that for most developers (except those developing exceptionally complex install profiles), they would be better served by having them in a simple format.

I am working on several issues regarding install profiles, and one of the issues I am working on is to make install profiles have access to all the drupal hooks, which would allow them to be extended via hook_info_alter.
see: #509404: Fix some conceptual problems with install profiles and make them actually usable

The other side effect of this would be that we could work towards making it possible for modules to add tasks during install.

I am aiming to make install profiles more like modules than they are now, and therefor I want to reduce the amount of additional hooks that are exclusively used in install profiles.

dries’s picture

Adrian, while I appreciate the install profile status update and the work you're doing (rock on!), I'm not sure how some of these comments are relevant to the patch. While install profiles can be improved so that they become more powerful and module-aware, there is always the initial Drupal install (e.g. installing the database, essential configuration, etc). Long story short, I'm not entirely sure what you're suggesting relative to this patch. Care to elaborate or to write a review?

adrian’s picture

Dries : David commented on this related issue of mine (http://drupal.org/node/509392#comment-1830106)

This patch reverts some of the changes from there. I should have linked back to the previous issue.

I simply stated my concern in that the patch was adding back a hook that I removed to make install profiles easier to write, and also made the implementation of the hook more complex. This goes against my goal of making install profiles more like modules, as I would like install profiles to be using the standard Drupal API as much as possible without inventing their own special case hooks that might be served just as well by existing hooks.

I fully intend on writing a proper review of this patch, and I've been in contact with David regarding the approach and possible difficulties as well.

dries’s picture

Ah, thanks for clarifying -- that helps. I don't know what you guys discussed in private, but I'm sure David (or you) will follow up. It sounds like it might have been an oversight/timing issue instead of an intentional re-introduction. I'm with you on making install profiles more like modules though! :) Looking forward to the follow-ups.

adrian’s picture

After discussion with David, I have come across an idea that I believe would simplify the process of automating Drupal installations, without being affected by the edge cases that install profiles can present :
#525594: Installation should consist of 2 phases instead of one

This mechanism would also be a preferred solution when building a hosted service such as acquia gardens or an aegir based solution, as it dramatically reduces the install profile specific information that is needed to be able to successfully provision a site.

boombatower’s picture

To followup in regards to PIFR.

Since PIFR runs in 6.x and will test an externally installed Drupal 7 site it needs to be able to install it without changing any code. I assume that is in the words are you wrote.

The current script is as follows.

  protected function install() {
    // Make a copy of the settings file to ensure there is no permission issue.
    $default_settings = $this->CHECKOUT_DIRECTORY . '/sites/default/default.settings.php';
    $settings = $this->CHECKOUT_DIRECTORY . '/sites/default/settings.php';
    copy($default_settings, $settings);

    require_once drupal_get_path('module', 'pifr_client') . '/review/browser.inc';
    $this->setPath();

    // Install Drupal.
    $b = new PIFRBrowser();

    // Step: Select an installation profile.
    // Step: Choose language.
    $b->drupalGet('install.php', array('query' => 'profile=default&locale=en'));

    // Step: Database configuration.
    $db_info = $this->getDatabaseInformation();
    $edit = array();
    if ($b->xpath('//input[@name="driver"]')) {
      $edit['driver'] = PIFR_CLIENT_DB;
    }
    $edit['database'] = $db_info['name'];
    $edit['username'] = $db_info['username'];
    $edit['password'] = $db_info['password'];
    $edit['host'] = $db_info['host'];
    $edit['db_prefix'] = '';
    $b->drupalPost(NULL, $edit, t('Save and continue'));

    // Step: Site configuration.
    $edit = array();
    $edit['site_name'] = 'checkout';
    $edit['site_mail'] = 'admin@example.com';
    $edit['account[name]'] = 'admin';
    $edit['account[mail]'] = 'admin@example.com';
    $edit['account[pass][pass1]'] = $this->adminPassword = $b->randomName(12);
    $edit['account[pass][pass2]'] = $this->adminPassword;
    $edit['update_status_module[1]'] = FALSE;
    $b->drupalPost(NULL, $edit, t('Save and continue'));

    // Step: Finished.
    $b->assertText(t('Drupal installation complete'));

    $this->setPath(TRUE);

    // Make sure that site installed correctly.
    if (($b->results['#exception'] + $b->results['#fail']) != 0) {
      $this->setError();
    }
  }

It would appear from reading that the re-factor supports what is needed, except as of now it require code modification to fill in the settings array. I assume is in the works to make something from command line that PIFR can just execute with parameters.

Overall, this appears to be exactly what is needed!

David_Rothstein’s picture

To summarize a couple other things I discussed with @adrian:

1. The issue with the tasks is that currently in Drupal HEAD, a regular Drupal install profile does not need to deal with the task system at all, whereas with my patch they would have to write code like this:

 /**
  * Implement hook_profile_tasks().
  */
function default_profile_tasks() {
  $tasks = array(
    'default_profile_site_setup' => array(),
  );
  return $tasks;
}

This is a bit unfortunate and would be nice not to have to do.

My patch is much better in the case of a more complex install profile. For example, for an install profile that implements an extra installation step with a form, my patch requires only this:

 /**
  * Implement hook_profile_tasks().
  */
function default_profile_tasks() {
  $tasks = array(
    'myprofile_settings_form' => array(
      'display_name' => st('My Profile settings'),
      'type' => 'form',
    ),
  );
  return $tasks;
}

And then myprofile_settings_form() is just a standard form API definition. I think this is a definite improvement over the current Drupal HEAD, where this kind of install profile would need to define this task in the .info file, rather than in a hook (that part is fine) but then would need to figure out on its own how to implement a "state machine" (that part's insane!).

I think that for now, the best way to get the benefits of both approaches is to look at @adrian's issue at #509400: Introduce .install files for install profiles. With that, simple profiles (that do not add anything to the user interface) would be able to put all their code in hook_install() and once again not need to deal with tasks at all, whereas more complex install profiles would be able to benefit from a more powerful and easier to use tasks system.

2. The second major issue we discussed had to do with scriptability of the Drupal installation. My patch works fine for simple profiles (for example, the code I posted at the top of this issue works exactly the same for the default and expert profiles - all you have to do is change 'profile' => 'default' to 'profile' => 'expert' and everything works the same). It also works fine for more complicated install profiles, provided the person writing the script knows which profile is going to be installed and what extra data it needs to receive beyond that normally required for a core installation.

It does not, however, allow you to write a generic installation script that works for any profile. If you tried to write such a script, certain profiles would cause your script to fail in such a catastrophic way that Drupal would never get installed at all. Getting around that limitation is the idea behind @adrian's proposal at #525594: Installation should consist of 2 phases instead of one -- as well as the fact that it would also enable other neat things, like a configuration wizard that runs when a new module is enabled on an existing site, etc.

In terms of the current patch, I don't think that idea affects things too much. Install profiles were not the main focus here -- the code I wrote works with them, but they could easily be taken out of this system later and moved elsewhere without changing much of the code. The vast majority of the code I wrote was there to support the core installation tasks.

The only possible simplification this might lead to down the line is that if we know every Drupal installation requires the same user input, then instead of doing programmatic form submission via drupal_form_submit() as the patch currently does, we might be able to have a command line script just ask for and save the data directly. However, currently profiles are allowed to modify the core "site configuration" form via hook_form_alter(), so we can't easily get rid of the form-based approach due to that.

If we could get rid of it, it would be nice, since currently the sample code I originally pasted in this issue requires a command line script to do things like this:

        'pass' => array(
          'pass1' => 'my_site_password',
          'pass2' => 'my_site_password',
        ),

which is less than ideal and could be avoided if we did not have to implement a form-based approach.

In summary, I don't personally foresee any major changes that would be needed to get this patch committable, although we do have some interesting followup issues that can be pursued.

David_Rothstein’s picture

A couple more technical notes from my conversation with @adrian - putting them here to remember to think about them later. Most of them have to do with issues that might arise with the command line installer trying to do things in one page request that are traditionally done over many requests:

  • We might need to make sure that modules are properly loaded (and their hooks are available) in the same page request in which they are installed, although hopefully D7 already takes care of that.
  • We might need to think about how to handle modules that implement hook_init(); if something later in the installation depends on that, it may need to be specifically called.
  • Although the non-interactive batch API calls implemented in this patch seem to work for me (caveat: I have not tested things like language imports) we might need to be more careful to to make sure they can never trigger a redirect.

None of the above things seem to cause any problems with the default or expert profiles at the moment, but they could potentially be an issue with other, non-core install profiles.

adrian’s picture

David , while I still haven't had time to review the patch, my only concern with the current approach (and it's really a nitpick), is that this refactoring doesn't really turn it into a command line script but turns it into an API.

If it was a command line script, i would be able to do :

php install.php sitename.com default --locale=en --db_user=user --email=username@place.com

From my perspective (aegir and drush), I would be served fine by the API-fication, but if this issue is about installing Drupal from the command line it feels unfinished unless you can actually install drupal from the command line without having to write additional code.

If this is solely about creating an API for install.php, I think that unless you can include install.php from within a command line script,
this patch in itself lacks usefulness.

I also don't notice a way to install a site other than sites/default with this patch, but i might have missed that in reading the code.
I will take the time to review this patch properly soon.

JacobSingh’s picture

StatusFileSize
new96.75 KB

Tested the patch, works great for me.

should probably add this to install.php:

if (realpath($_SERVER['SCRIPT_FILENAME']) == __FILE__) {
install_drupal();
}

This will allow a user to include install.php while not actually running it.

So the example above can run as a standalone script.

Ideally, the constants should either do a defined() check or be inside a block somewhere as well so they can be safely included.

I personally feel that an API is a good first step, and is worth committing. If we build a CLI app later, great, we still want an API. But also, I don't think a command line app is really useful. There would be so many damn switches, it would start to look like ffmpeg. I'd personally prefer a settings file I can pass in, and perhaps some overrides at a first iteration.

What I would change (although I think we should commit what we've got ASAP), is the code design. If it is an API, it should behave like one IMO, which means more than just one function, some validation, etc.

$di = new DrupalInstaller();
$di->setDomain();
try {
$di->setDatabaseDetails();
} catch DrupalInstaller_NoDBException() {
$di->createDatabase();
$di->setDatabaseDetails();
}

Anyway, that's not important for now. RTBC IMO.

Best,
Jacob

JacobSingh’s picture

Oh, one other nitpiky thing... the settings array has pass1 and pass2. That's just kinda silly.

-J

adrian’s picture

I've read through the patch and I agree 100% with the approach, it's a lot cleaner and a lot simpler. I actually ended up writing my own wrapper around hook_profile_tasks, which ended up working very similarly.

I haven't done a functional test of the code yet however, and I'm still a bit unclear about handling pages that return plain text.

If the change that jacob suggests is implemented (ie: switch to stop the script from running when included), i support it to be RTBC

adrian’s picture

This makes one very simple change to dbapi btw, which makes it a lot simpler to provide clean log information.

If we could get another review I'd RTBC it. moshe? dries?

anarcat’s picture

I have reviewed the patch quickly but I feel I am not in a position to confirm that it is proper as I do not know well enough the internals of the D7 install process. I do however fully support the inclusion of this patch into core as it will make things much easier for us (Aegir/Drush). The code does seem pretty sound. (Plus I want to subscribe to the issue. :)

webchick’s picture

Status: Needs review » Needs work

The documentation of this patch is fantastic. It's a really big change diff-wise, but the code that's left over is very easy to read and follow IMO. I also really like the move to exception-based error handling rather than compiling a bunch of #markup code.

+define('INSTALL_SKIP_TASK', 1);
+define ('INSTALL_RUN_TASK_IF_REACHED', 2);
+define('INSTALL_RUN_TASK_IF_NOT_COMPLETED', 3);

Let's namespace these so they appear grouped together on api.drupal.org. INSTALL_TASK_XXX. Also, minor, but there is an extra space between define() on #2. The PHPDoc should also be improved to indicate to developers when they might need to use these constants.

+ * @param $settings
+ *   An optional array of installation settings. Leave this empty for a normal,
+ *   interactive, browser-based installation intended to occur over multiple
+ *   page requests. Alternatively, if this is passed in, the installer will
+ *   attempt to use it to perform the installation in a single page request
+ *   (optimized for the command line) and not send any output intended for the
+ *   web browser.

If "this" is passed in? What exactly is "this" that I pass in to get this to work from the command line? Is there another function that explains this? If so, let's add a reference to it.

install_state_defaults()

Congratulations on the most well-documented function in all of Drupal! :D That's great!

+    // An array of forms to be programmatically submitted during the
+    // installation.
+    'forms' => array(),

Some more info here? Is this an array of form callbacks, or an array of $form_state['values'] arrays or..?

+    // This becomes TRUE only at the end of the installation process; certain
+    // theme functions may have access to it then.
+    'installation_finished' => FALSE,

Could you define what it means to be at the end of the installation process? Is this after all tasks have completed, or all tasks plus import of languages (I saw a distinction elsewhere), or..?

+    // An array of parameters for the installation, pre-populated by the URL
+    // or by the settings passed in to install_drupal().
+    'parameters' => array()

Can I get a for example? locale and profile? if it's always URL parameters, could we maybe make this url_parameters rather than just parameters?

+    // Installation tasks can set this to TRUE to force the page request to
+    // end (even if there is no themable output), in the case of an interactive
+    // installation.
+    'stop_page_request' => FALSE,

Why?

+    // Installation tasks can set this to TRUE to indicate that the task should
+    // be run again, even if it normally wouldn't be.
+    'task_not_complete' => FALSE,

Why?

+      'run' => INSTALL_RUN_TASK_IF_REACHED,

This seems to be in there an awful lot. Should that be the default value?

-  install_tasks($profile, $task);
+    $install_state['database_tables_exist'] = TRUE;

Indentation off here.

+    if (isset($install_state['parameters']['profile']) && isset($install_state['parameters']['locale'])) {
+    $filename = 'profiles/' . $install_state['parameters']['profile'] . '/translations/' . $install_state['parameters']['locale'] . '.po';
     if (file_exists(DRUPAL_ROOT . '/' . $filename)) {
       require_once DRUPAL_ROOT . '/includes/locale.inc';
       $file = (object) array('filepath' => $filename);
       _locale_import_read_po('mem-store', $file);
       $locale_strings = _locale_import_one_string('mem-report');
     }
+    }

Indentation looks off here.

+  return st('We were unable to find any installer profiles. Installer profiles tell us what modules to enable and what schema to install in the database. A profile is necessary to continue with the installation process.');

I know you didn't write this string, but can we please change those to "installation profiles" for consistency with everything else?

-  print theme('install_page', $output);

I only see - signs next to all calls to theme('install_page'). Did we lose the ability to theme the installer as part of this patch?

+if (realpath($_SERVER['SCRIPT_FILENAME']) == __FILE__) {

Had I not read one of the replies in this thread, I'd have no idea what that was doing. Please add a comment to explain.

+function drupal_override_server_variables($variables = array()) {

This function is a flaming wtf to people who don't spend their lives monkeying about with command-line PHP. Can you add some more information to the PHPDoc as to why this function is needed?

Also, run-tests.sh and drupal.sh in /scripts both seem to have parts very similar to this. Can we replace those with calls to this function instead? And is there stuff from those scripts that needs to be replicated here?

+function cache_get_multiple(array &$cids, $bin = 'cache') {
+  return FALSE;
+}
+
 function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL) {
   return;
 }

I have no idea why that is here. I see that there's already a function with no PHPDoc explaining below it, so probably not for this patch to change, but why are you returning FALSE instead of nothing like the one below?

+ * In general, each task function you define should return content to be
+ * displayed to the end user, if you want the page request to end. You should
+ * also set custom page titles within the task function, if necessary.
+ * Alternatively, if you want to pass control to the next task without ending
+ * the page request, do not return any output.

Overall, these comment improvements are fantastic! However, since realistically this is the only spot in all of core where we describe the installation process, a bit more overview of how it works here would be good, as I'm still left with questions. How do I know if I want a page request to end? How do I know if setting custom page titles is necessary?

+ * Remember that a user installing Drupal interactively will be able to reload
+ * the pages multiple times, so you might want to use variable_set() and
+ * variable_get() to remember your data and control further processing. It is
+ * important to remove any temporary variables using variable_del() before your
+ * last task has completed and control is handed back to the installer.

Again, this is a little wishy-washy. Why might or might not I want to use variable_set/get?

+ *     - 'display'
+ *       A boolean which can be set to force the task to either display or not
+ *       display to the end user. This can be used to provide finer-grained
+ *       control over whether or not the task will display.

Well, I can guess that from the name. ;) What does that actually mean, though? display_name hides a task from the list, this hides it from... the... screen? Then why bother having it in the first place? What's the use case?

+ *     - 'type'
+ *       A string representing the type of task. If this is left unset or set
+ *       to 'normal', the task will be treated as a regular callback function,
+ *       which does its processing and optionally returns HTML output. If this
+ *       is set to 'batch', the task function should return a batch API
+ *       definition suitable for batch_set(), and the installer will run the
+ *       task via batch processing. Finally, if this is set to 'form', the task
+ *       function should return a standard form API definition (and separately
+ *       define validation and submit handlers, as appropriate), and the
+ *       installer will appropriately direct the user through the form
+ *       submission process.

Could we itemize the list of possible values as a sub-list so it's a bit easier to scan? See hook_menu() for an example.

+ *     - 'run'
+ *       A constant representing the manner in which the task will be run. If
+ *       this is left unset or set to INSTALL_RUN_TASK_IF_NOT_COMPLETED, the
+ *       task will run once during the installation of the profile, until the
+ *       installer marks it complete. If this is set to INSTALL_SKIP_TASK, the
+ *       task will not run during the current installation page request.
+ *       Finally, if this is set to INSTALL_RUN_TASK_IF_REACHED, the task will
+ *       run on each installation page request that reaches it.

Same here. And can you help me again with the "why"?

+ * @see install_state_defaults()

We should probably also add a @see batch_set() to explain the desired format of a batch array.

+  // This is an example of a variable that your profile might set and unset
+  // throughout the tasks below, to control which tasks actually need to be
+  // processed.
+  $myprofile_needs_batch_processing = variable_get('myprofile_needs_batch_processing', FALSE);

I was confused reading this (figured it out a few lines later). How about adding something like, "Here, we define a variable to allow tasks to trigger a batch process if they are about to start a processor-intensive task."

There are no extra
+    // steps required for your profile to act as a multi-step installation
+    // wizard; you can simply define as many tasks of type 'form' as you wish
+    // to execute.

YAY!!! :D Just a question. Does it make sense to generalize this out in another issue to something modules can use as well? I don't know anyone but Jeff Eaton who can explain how multistep forms work currently. :P

+    // This is an example of a task that will not be displayed in the list that
+    // the user sees. To implement this task, your profile would define a
+    // function named myprofile_final_site_setup(), in which additional,
+    // automated site setup operations would be performed. Since this is the
+    // last task defined by your profile, you might also use this function to
+    // call variable_del('myprofile_needs_batch_processing') and clean up the
+    // variable that was used above. You might choose to return no output from
+    // this function, if you want the user to pass to the final Drupal
+    // installation tasks uninterrupted, or you could return themed output that
+    // the user will see (for example, a confirmation page explaining that your
+    // profile's tasks are complete, with a link to reload the current page and
+    // therefore pass on to the final Drupal installation tasks when the user
+    // is ready to do so).

This chunk has a lot more of these sort of wishy-washy "might" recommendations. Each time you use that word it leaves me with the question "Well why might I NOT want to do that?" It seems like at least calling variable_del() is a *should* and not a *might*. Maybe rephrase the second part as "if" rather than "might": "If you want the user to pass to the Drupal installation tasks uninterrupted, return no output from this function. Or, if you wish to..."

moshe weitzman’s picture

In a follow-up issue, we should really create a new install profile that really configures your site for a blog or a knowledgebase or something. Once plugin manager is in core, is it legit for core profiles to download contrib modules? Good times.

JacobSingh’s picture

@moshe weitzman

Sadly, #395480: Plugin Manager in Core: Part 4 (installation profiles) has not started yet. I hope we can get in, but the process has been rather slow given how straight forward most of the engineering is. Hopefully it will get more streamlined as people realize the deadline is fast approaching. If you would care to donate any precious time to the effort, #395474: Plugin Manager in Core: Part 2 (integration with update status) is pending fixes to make it work in Windows, and something from catch to prioritize minor over major versions.

Sorry to take this off topic.

Best,
Jacob

David_Rothstein’s picture

Status: Needs work » Needs review
StatusFileSize
new102.64 KB

Thanks for the reviews! I've attached a version with changes made based on @webchick's comments.

Also here are a few things that need responses, for both that review and the other ones:

+    // An array of parameters for the installation, pre-populated by the URL
+    // or by the settings passed in to install_drupal().
+    'parameters' => array()

Can I get a for example? locale and profile? if it's always URL parameters, could we maybe make this url_parameters rather than just parameters?

I added the example as requested, but for now I did not change the name to 'url_parameters' since the URL is only relevant in the case of browser-based installations (not command line ones), and I think we want to keep this as general as possible.

+      'run' => INSTALL_RUN_TASK_IF_REACHED,

This seems to be in there an awful lot. Should that be the default value?

This is definitely used several times, but mainly very early on in the installation process, only by Drupal core. I think it is better to leave the default as the one that install profiles are most likely to use; that way they don't have to worry about specifying it each time they write a task.

Indentation off here.

Yes, and that is only the most visible example of it -- there are many others :) As mentioned originally, I did this on purpose in order to try to keep the patch size manageable... for example, a lot of code in this patch needed to get moved from if() statements to individual functions, so those would have been gigantic blocks of changes that would have made the patch unreviewable. I could fix them now if you want, but that will make the patch file a lot bigger. (Alternatively, if this gets committed as is, I promise to do a very quick followup patch that fixes all the indentation!)

I only see - signs next to all calls to theme('install_page'). Did we lose the ability to theme the installer as part of this patch?

Nope, all the themed output has been consolidated into the install_display_output() function, which contains this line:

  print theme($install_state['database_tables_exist'] ? 'maintenance_page' : 'install_page', $output);

(Note that the switch from 'install_page' page to 'maintenance_page' partway through attempts to mimic what the installer is already doing before this patch is applied; I'm suspicious about whether it is necessary at all and whether we couldn't just use 'maintenance_page' always, but that would be a topic for a followup issue, I think.)

+if (realpath($_SERVER['SCRIPT_FILENAME']) == __FILE__) {

Had I not read one of the replies in this thread, I'd have no idea what that was doing. Please add a comment to explain.

Right, this was added by Jacob as a neat little way to allow install.php to be includable by a command line script, which needs to happen currently since a bunch of API functions are in that file. In the long run I'm guessing we want to move those functions out of install.php and into a separate includable file, so I did add a comment to the code to explain it, but I phrased it more as a TODO.

Also, to respond to what adrian wrote, I agree that this issue is really more about adding an API than about having a command line script directly. It certainly wouldn't be hard to have a followup issue that added an install.sh type of script to core, though (with the caveat that it would only work for simple install profiles, as mentioned earlier). Probably Drush is the perfect place for this kind of thing, however, since as Jacob mentioned, a command line script would involve passing in a whole bunch of parameters, and Drush is well-equipped to handle that sort of thing, e.g. via a drushrc config file that can remember them for you.

+function drupal_override_server_variables($variables = array()) {

This function is a flaming wtf to people who don't spend their lives monkeying about with command-line PHP. Can you add some more information to the PHPDoc as to why this function is needed?

I gave it a whirl, although I'm not quite sure I got it right; someone else who has done this more might want to take a look too.

By the way, this also answers @adrian's question about installing sites in places other than sites/default with this patch -- although I haven't actually tried it myself, in theory it should be possible to do that by passing $settings['server']['url'] as part of the array that gets sent to install_drupal().

Also, run-tests.sh and drupal.sh in /scripts both seem to have parts very similar to this. Can we replace those with calls to this function instead? And is there stuff from those scripts that needs to be replicated here?

I agree it would be a good idea to use this function in those scripts as well, but I'm hoping that could be left to a followup patch since there could be some subtleties there...

I am honestly not sure exactly which $_SERVER settings do and don't matter in various cases; I sort of built up this function by borrowing from the existing scripts (as well as from Drush), and basically filling things in each time I encountered a new PHP error while testing out the command line installation :) It would certainly be great if someone who understands this better could take a closer look at what I put in drupal_override_server_variables() and see if it makes sense.

+function cache_get_multiple(array &$cids, $bin = 'cache') {
+  return FALSE;
+}
+
function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL) {
   return;
}

I have no idea why that is here. I see that there's already a function with no PHPDoc explaining below it, so probably not for this patch to change, but why are you returning FALSE instead of nothing like the one below?

The reason that is in this patch is basically a bugfix; cache-install.inc is supposed to include stub implementations of all cache functions, but evidently cache_get_multiple() got added recently without that happening. It turns out that its absence only causes a problem for command line installations, since regular installations start using the real Drupal cache system once the database exists (and therefore only use the stub implementation towards the beginning of the installation), but a command line install which happens in one page request sticks with cache-install.inc throughout, and it turns out that cache_get_multiple() gets called somewhere towards the end of the installation (I think it was in Field API), so we need to add it to avoid a fatal error.

I used FALSE to match what is returned by cache_get(), but actually I switched it to array() in the attached patch, since I realized that's what the PHP doc in cache.inc says it is supposed to return. It probably doesn't matter what it returns as long as it's empty. Anyway, this whole file could probably use a cleanup, since it seems Drupal's cache system has changed a lot recently, and now involves implementing an interface, etc.

There are no extra
+    // steps required for your profile to act as a multi-step installation
+    // wizard; you can simply define as many tasks of type 'form' as you wish
+    // to execute.

YAY!!! :D Just a question. Does it make sense to generalize this out in another issue to something modules can use as well? I don't know anyone but Jeff Eaton who can explain how multistep forms work currently. :P

Yes, I think it's possible that kind of generalization could occur as a result of #525594: Installation should consist of 2 phases instead of one. However, I may have oversold it a bit in my code comments; this is not a "true" multistep form because the $form_state['values'] don't accumulate and get passed along. It is really just a bunch of totally different forms that run one after another. I edited the code comment slightly so that it's less likely to get people's hopes up :)

Jaza’s picture

StatusFileSize
new109.13 KB

Nice patch. I gave it a whirl, and it's all working smoothly for me.

However, I think that if we're going to support command-line installation, then we should provide at least the bare minimum to allow it to be used without any supporting third-party script (e.g. Drush, PIFR). Of course, anything advanced should be left to those third-party scripts. But the current advice given to testers of this patch — i.e. "hack the bottom of install.php and replace install_drupal() with (settings) code" — is IMHO a pretty clear sign that we need to do more out-of-the-box.

Therefore, I've extended this patch with the following features:

  1. Support for loading install settings from a file. The contents of the file should be the $settings array needed to configure the installer, nothing more. This file defaults to sites/default/settings.install.php. The default can be overridden, by passing in a filepath as a command-line argument, e.g. calling php install.php sites/site2/settings.install.php. This should ease people's worries about having to add a gazillion command-line options to install.php: all I'm suggesting is that we add one (and an optional one, at that).
  2. Added a sites/default/default.settings.install.php file.
  3. Feedback gets printed to the command-line for success or for errors. If all goes well, it simply prints Drupal installed successfully. If any exception is thrown by the installer, it is caught and printed out (HTML is stripped, newlines are inserted, entities/symbols are decoded). If the settings.install.php file isn't found, or doesn't contain a $settings array, an error and some useful help is printed.

(PS: the install_is_cli() function is borrowed from Drush's drush_verify_cli() function.)

With these enhancements, it will be possible to download Drupal, to create a settings.install.php file, and to run:

php install.php

And that's it.

I'm anticipating that many people will consider this to be feature bloat and to not belong in the core installer. To those of you in that camp, I have to disagree: it's currently not even possible to test this patch without an additional hack to install.php. This enhancement at least makes it possible to test a command-line install in 5 seconds. However, I can understand if you insist that this be split into a separate follow-up patch.

Status: Needs review » Needs work

The last submitted patch failed testing.

JacobSingh’s picture

@Jaza: Nice stuff. I actually did something similar and decided not to add it because I thought it a separate issue.

You can in fact test this patch without hacking install.php.

See the comments above, I added a little condition to check if the script is being run directly or included. This means you can write your own install script which just looks like this:


include_once 'install.php';

$settings = array()...

install_drupal();

Best,
J

David_Rothstein’s picture

Status: Needs work » Needs review
StatusFileSize
new103.48 KB

Thanks, @Jaza -- I like what you added a lot, but I'm also going to vote for making it a followup issue. Partly because this is already a 100K patch even without that, but also, like Jacob said, the code he added already lets the file be included so anyone can try this out with any kind of script they want. If we want to put a script in core, it should probably live in the scripts directory and use the same kind of mechanism. But I think the code you wrote is a great start at that - I especially like cleaning up the error messages so that they don't spit out HTML to the command line.

Anyway, the biggest issue is that the testbot is currently rejecting your patch for some reason that I don't immediately understand, whereas I'm pretty sure mine will still work :) So here is a re-uploaded version of the patch from #27. I also made some additional typo fixes in the code comments, and added a CHANGELOG.txt entry.

Jaza’s picture

@David_Rothstein: sounds good to me. I'm happy to work with yourself and @JacobSingh on this in a follow-up patch.

I also can't understand why my patch gives a 'failed to install HEAD' error. But since I have no idea how PIFR works, I assume that something I did is tripping up the way PIFR triggers the installer. Will leave that problem for another day.

I gave your latest patch a whirl, and it's still working great. +1 from me.

dries’s picture

Instead of creating a new settings file, can't we re-use the existing settings.php?

As a developer, I'd love to be able to do something like: "./install.php --truncate" or something to start with a clean site. Pretty destructive, i know so maybe I should leave that as a custom script. ;-)

Will do a review shortly, if webchick doesn't beat me to it.

boombatower’s picture

I agree with Dries having a "truncate" type functionality would be really nice, especially when working on Drupal HEAD and you mess something up or CVS update requires a re-install.

adrian’s picture

For provision / aegir, it would actually be better for us if it just used the settings.php we created instead of creating the config file itself.

so the process for installing a site is a) creating the settings.php file, b) running the script.

David_Rothstein’s picture

Not sure I understand the last few comments - isn't that the way the installer always worked? The patch didn't do anything to change that.

If you create a settings.php file by hand (that has the correct database information already in it), or if you blow away the database on an existing site while leaving the settings.php file behind, the installer will skip creating settings.php and use what's already there.

I'm not sure about shipping the "DROP DATABASE" functionality with core, though :)

hass’s picture

I wish there would also be a command line update.php... to remove bad fun - updating 500 sites every few weeks...

adrian’s picture

@hass - there already is, check out drupal.org/project/drush

we should definitely tackle making update.php includable next though

David_Rothstein’s picture

StatusFileSize
new100.28 KB

Patch rerolled to chase HEAD (due to changes introduced by #349508: Require UTF8 database encoding).

Anonymous’s picture

Status: Needs review » Reviewed & tested by the community

I've tested several times, both interactively and through script, to see if I could break the patch. Each time I used a variety of different options. No dice.

The patch works. ;)

boombatower’s picture

Once this goes in...either roll a patch for PIFR...or just make sure I am notified and I'll get it in as quickly as possible.

dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed this to CVS HEAD. Coordinated with DamZ so that he disabled the test bot for now. Thanks all!

David_Rothstein’s picture

Status: Fixed » Needs review
StatusFileSize
new25.75 KB

Cool!

Here is the promised followup patch, which should fix all the whitespace/indentation issues mentioned above.

I thought about combining this with moving the code from install.php to a separate, includable file, but then realized we might want to think a bit more how to handle the defined constants when doing so -- related to the point Jacob brought up in #19, there is a particular problem with the MAINTENANCE_MODE constant, since if that is defined the rest of Drupal will automatically assume the site is in installation mode. So if we simply moved that define() to the top of a new includes/installer.inc, then anyone merely including that file would automatically trigger the rest of Drupal to be installation mode, which is not good for an API.

We could move the define() into a function, although I don't think Drupal does that anywhere else? Or we could simply require that any calling script has to define MAINTENANCE_MODE on their own (similar to the way that scripts currently have to define DRUPAL_ROOT), but that seems pretty ugly. Any ideas?

Anyway, the attached patch should be a pretty quick commit, and then once we get the includes/installer.inc issue done we can move on to the "real" followups.

David_Rothstein’s picture

By the way, I contacted @boombatower separately, but as far as I know this patch did not break the testbot.

(We might want to have a followup to change PIFR to use the new method, but it shouldn't be required for the testbot to work correctly, since the browser-based installation method that PIFR currently uses works the same way as it did before.)

damien tournoud’s picture

MAINTENANCE_MODE is a bootstrap-time constant. It belongs into the entry point (for example install.php).

moshe weitzman’s picture

I would personally move maint mode into a function and actually let it get unset if the caller knows what it is doing.

anarcat’s picture

Status: Needs review » Reviewed & tested by the community
StatusFileSize
new5.46 KB

I have reviewed the patch and it looks fine to me. I have attached a new version of the patch that removes the whitespace changes to enhance readability. My patch obviously shouldn't be tested or considered for inclusion in HEAD, I just include it so that people can see more easily what are the non-whitespace changes (because there are a few, because of word wrapping) in the patch.

I think this should go in, in short.

dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed #47 to CVS HEAD. Maybe we should create a new patch for the follow-up work?

boombatower’s picture

Status: Fixed » Reviewed & tested by the community
boombatower’s picture

Status: Reviewed & tested by the community » Fixed

Cross post.

anarcat’s picture

Re: #48 @adrian, the issue for update.php is #233091: Move code from update.php into includes/update.inc.

Otherwise, is this complete? I feel a lot of functions still sit in install.php and while the patch committed makes it possible to include install.php, it's still a sub-standard/hackish API. We should move the functions to install.inc.

Indeed, the end of install.php currently lists:

// TODO: This if() statement allows Drupal to be installed interactively when
// install.php is visited in a web browser, while simultaneously allowing the
// file to be included by command line scripts so that it can be used as an
// API. It should be removed after the API functions in this file have been
// moved out to a separate, reusable location.
if (realpath($_SERVER['SCRIPT_FILENAME']) == __FILE__) {
  // Start the installer.
  install_drupal();
}

Quite a hack... Ideally, install.php would be only a few lines long and would handle forms and things like that... Should a new issue be opened for this or should we reopen this one?

David_Rothstein’s picture

Status: Fixed » Needs review
StatusFileSize
new141.54 KB

Yes, that was what I was thinking - get the file reorganization committed quickly in this issue, and then at that point we are done the gigantic changes so we can close this and open some (normal) followups.

We also need to do something here since it looks to me like Dries committed the "reviewer only" patch in #47 rather than the actual patch in #43 - oh well :) So there are lots of whitespace fixes that didn't yet go in.

I think we can combine both of the above and have done so in the attached patch. To make this, I:

  1. Reverse-applied the patch from #47 and then applied the patch from #43 so that we now have all the whitespace fixes.
  2. Moved just about everything from install.php to a new includes/installer.inc file.
  3. Added the standard @file/etc stuff at the top of both install.php and includes/installer.inc.
  4. Removed the if statement/TODO mentioned in #51, since install.php itself is now only intended for interactive, browser-based installations.
  5. Moved the following line from the top of install.php (where it was a standalone line of code) into the install_begin_request() function, where the other installer-related files get included: require_once DRUPAL_ROOT . '/includes/install.inc';

For now, I went with Damien's suggestion of leaving MAINTENANCE_MODE in install.php, since it's simplest. I think Moshe's idea sounds right, but if I understand it correctly it involves getting rid of the define() altogether, so that probably could use a separate followup issue since it also affects update.php, etc.

Hopefully we can get this in quickly, since obviously it will break if anything else gets committed (and I don't think there are any large installer patches on the horizon that this will conflict with).

kika’s picture

It's not very clear by looking at file names what does installer.inc and what install.inc. More logical names perhaps in future bikeshedding followup?

dries’s picture

Like kika, I agree that installer.inc is a confusing name.

David_Rothstein’s picture

Personally I think "install.inc" is actually the confusing one -- since it contains some random mix of utility functions, functions that are used only by update.php, etc.

I have no great ideas for a renaming at the moment.

anarcat’s picture

I don't think installer.inc is confusing. It's the main installer UI. Install.inc should be a separate file that contains the Drupal Install API, and it should be clearly labeled as such.

I absolutely love seeing install.php reduced to 2 lines and support this patch. I haven't tested it so I'm not marking it as RTBC, but I strongly suggest kicking this in so we unblock the other installer work (like #525594: Installation should consist of 2 phases instead of one).

Status: Needs review » Needs work

The last submitted patch failed testing.

David_Rothstein’s picture

I'll gladly reroll this once we have a commitment that it can go in... If no one can come up with a better file naming suggestion now, can we postpone that discussion to a followup?

Like @anarcat said, there are other issues that would benefit from this going in first. It also makes it easier to actually install Drupal via the command line -- currently, I think any custom script would have to be called from the top of the Drupal root directory in order to work (since you need to include install.php which forces its own definition of DRUPAL_ROOT on you, etc).

cweagans’s picture

Right, so we have a small issue here:

Since this was committed, I haven't been able to install core. I've talked to webchick and ksenzee in IRC and they said to create a new issue (#546390: Fix CLI detection in install.php), but I thought I'd bring it up here since the problem area was in this patch.

My problem is that, from a browser, realpath($_SERVER['SCRIPT_FILENAME']) is returning FALSE, so the installer does not run. IMHO, install.php needs to be another two-liner file with all the install functions moved to the includes dir fairly quickly.

moshe weitzman’s picture

Yes, I think we can defer naming changes. Even kika suggests it ... My .02 are for install_ui or install_wizard

David_Rothstein’s picture

I can reroll the patch from #52 sometime tomorrow if no one beats me to it.

I think I like "install_wizard.inc"... maybe a little inaccurate when called from a command line script (since it's not much of a "wizard" in that case), but it does seem to get the point across.

JacobSingh’s picture

My bad, I thought what I put in was the best method... Not sure why it isn't working, but anyway:

From: http://www.codediesel.com/php/quick-way-to-determine-if-php-is-running-a...

 
function isCli() {
 
     if(php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR'])) {
          return true;
     } else {
          return false;
     }
}
 

Might be better.

David_Rothstein’s picture

Status: Needs work » Needs review
StatusFileSize
new142.95 KB

OK, here is the rerolled patch from #52. I used "install_wizard.inc" as per Moshe's suggestion. It's easy to do a quick switch to something else right before committing (just do a find-replace on the patch file), but other than that, I agree, it would be great to defer any further discussion on the name until later.

So let's see if we can get this RTBC and committed as quickly as possible :)

dries’s picture

Instead of using wizard, I'd prefer to use something that we can use consistently for other .inc files. Maybe we should use MVC naming patterns? install.view.inc, install.controller.inc, install.model.inc ?

David_Rothstein’s picture

Dries, I think #316916: Create install directory with multiple files and modularized code might be the issue for that.

As things stand, both install.php and install.inc each contain a big mix of stuff, so I'm not sure which MVC-based renaming we'd use here, without totally reorganizing the files, which seems out of scope for this issue :)

JacobSingh’s picture

I'm confused, what is the problem here?

If you determine that the script is being called from the browser, isn't that good enough to distinguish between an include and a hit from a browser directly?

boombatower’s picture

Just reporting my results in trying to use this by SimpleTest and PIFR.

PIFR:
Need a way to call this externally, without having to be inside the Drupal 7 codebase. Looks like there might be work to make CLI tool, not sure. I could hack and insert a file into the D7 checkout that purely executes the installation code, but that seems a bit hackish, no more so then what it currently does I suppose.

SimpleTest:
The code needs to be separated into two parts: 1) wizard that syncs with forms and does all the validation of input and settings directory, etc; 2) API code the does what it is told regardless of conditions.

The reasoning behind this is that SimpleTest needs to use the install code to generate a new DB environment, but doesn't want to mess with the settings.php files and such. You can see what I had to do in order to get the code to work in #488182: Shared test runner between webrunner and script and perfectly clean test environment. A hack that I was hoping would be fixed by the time I finished patch.

I haven't read all the comments, so I'm not sure if this stuff is in the works on already in the patch, but it seems that these items make sense to be address so that this code can be utilized (especially the SimpleTest portion as that is necessary for other implementations such as UTS and makes sense in general. We separate API from forms and things work better.).

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

Lets keep things moving here.

dries’s picture

Status: Reviewed & tested by the community » Needs review

I'm not sure why we need to keep this moving. As far as I understand, it doesn't block anything. It is merely a cosmetic patch, and one that I don't necessarily agree with in its current shape.

Status: Needs review » Needs work

The last submitted patch failed testing.

eaton’s picture

moshe weitzman’s picture

It isn't cosmetic. drush can't easily use this as is. install.php *forces* a DRUPAL_ROOT value, for example.

Any chance for a re-roll, David?

dmitrig01’s picture

Also, good luck installing Drupal from the command line without this patch, no matter which installer (Aegir, Drush, Acquia hosting). SimpleTest part can easily be a follow-up

boombatower’s picture

If this is abstracted properly. Right now the installer does way to much validation, such as checking for existing db (without reguard to prefix) and for settings.php file. It needs to be split into two aparts.
1) installer.php with validation and such
2) installer.inc pure API which does what its told.

Otherwise SimpleTest cannot use this. Even if command line installer exists, if it checks the stuff above then it won't work for SimpleTest.

David_Rothstein’s picture

Status: Needs work » Needs review
StatusFileSize
new24.8 KB

Let's start by getting the whitespace fixes out of the way. Those are uncontroversial and were supposed to have been committed above anyway -- and they are what has been making rerolling the larger patch painful.

@boombatower: While it would be nice to split the installer into two parts along those lines, I don't think it's necessary for getting the installer to work with SimpleTest. The main thing we would need to do is simply, as you say, enable non-interactive installations to work with a different DB prefix, which should be a small change. We should create a separate issue for this (if there isn't one already). I've sort of been assuming that this is the sort of thing that could be done after code freeze, even if it did involve a small change to the API (since the primary purpose would be to improve test coverage), but if not... well, if not, it probably isn't going to happen for Drupal 7 :)

webchick’s picture

Status: Needs review » Needs work

Yay! I love whitespace clean-ups. ;) Committed #75 to HEAD.

This will need a re-roll.

David_Rothstein’s picture

Status: Needs work » Needs review
StatusFileSize
new143.04 KB

OK, here is a reroll of the patch that moves everything out of install.php.

1. I decided to go with "includes/install_drupal.inc" as the new filename this time - seems simplest :) Again, this can easily be changed by directly editing the patch file.

2. Somewhat annoyingly, I found that even with this patch, a command line script still needs to chdir() to the Drupal root directory in order to successfully complete the installation. The reason is that there appear to be lots of places in core that ignore DRUPAL_ROOT and use relative paths -- an example is in http://api.drupal.org/api/function/drupal_get_filename/7 which simply calls file_exists() on the passed-in relative filename. Not sure if these are known issues or not, but either way, they are bugs and can be dealt with after code freeze.

anarcat’s picture

Assuming the copy-paste was done properly (which it looks like from a quick review, not byte per byte), this patch looks fine and "compiles" (ie. I can still install Drupal with it applied).

Update: I have reviewed the actual diff between the original install.php and the new insta_update.inc and it looks fine to me. Here's the resulting diff, which *should not* be applied and is provided here simply for peer review:

--- install.php	2009-09-01 05:24:06.000000000 -0400
+++ includes/install_drupal.inc	2009-09-01 05:23:13.000000000 -0400
@@ -1,17 +1,11 @@
 <?php
-// $Id: install.php,v 1.202 2009/08/25 21:53:46 dries Exp $
+// $Id$
 
 /**
- * Root directory of Drupal installation.
+ * @file
+ * Defines an API for installing Drupal, either interactively or via a command
+ * line script.
  */
-define('DRUPAL_ROOT', getcwd());
-
-require_once DRUPAL_ROOT . '/includes/install.inc';
-
-/**
- * Global flag to indicate that site is in installation mode.
- */
-define('MAINTENANCE_MODE', 'install');
 
 /**
  * Global flag to indicate that a task should not be run during the current
@@ -240,6 +234,7 @@
   require_once DRUPAL_ROOT . '/modules/system/system.install';
   require_once DRUPAL_ROOT . '/includes/common.inc';
   require_once DRUPAL_ROOT . '/includes/file.inc';
+  require_once DRUPAL_ROOT . '/includes/install.inc';
   require_once DRUPAL_ROOT . '/includes/path.inc';
 
   // Set up $language, so t() caller functions will still work.
@@ -1708,13 +1703,3 @@
   // Record when this install ran.
   variable_set('install_time', $_SERVER['REQUEST_TIME']);
 }
-
-// TODO: This if() statement allows Drupal to be installed interactively when
-// install.php is visited in a web browser, while simultaneously allowing the
-// file to be included by command line scripts so that it can be used as an
-// API. It should be removed after the API functions in this file have been
-// moved out to a separate, reusable location.
-if (php_sapi_name() != 'cli' && !empty($_SERVER['REMOTE_ADDR'])) {
-  // Start the installer.
-  install_drupal();
-}
anarcat’s picture

Status: Needs review » Reviewed & tested by the community

This is the final phase of refactoring of install.php, and the fulfillment of the following TODO:

-// TODO: This if() statement allows Drupal to be installed interactively when
-// install.php is visited in a web browser, while simultaneously allowing the
-// file to be included by command line scripts so that it can be used as an
-// API. It should be removed after the API functions in this file have been
-// moved out to a separate, reusable location.

As such, since it's an API change of some sort (well, the functions move from a different file) I think it should go in before the freeze (today?) so that we know where to find the install functions in the long term.

Also note that similar work was done on update.php (#233091: Move code from update.php into includes/update.inc) so it would just make sense to finish the work here and keep install.php small (like update.php).

Now to specifically adress this:

I'm not sure why we need to keep this moving. As far as I understand, it doesn't block anything. It is merely a cosmetic patch, and one that I don't necessarily agree with in its current shape.

@Dries - could you expand on what you don't agree with in the current patch?

Thanks,

moshe weitzman’s picture

Right - this is just moving code around to match update.php. Lets please commit or elaborate on objections.

Status: Reviewed & tested by the community » Needs work

The last submitted patch failed testing.

David_Rothstein’s picture

Well, I'm amazed that it lasted over two weeks :)

I'm really not sure what to do here, but constantly rerolling the patch (it basically requires a complete manual reroll each time) is a waste of time. Should we wait until the RTBC queue is smaller to try this again?

We all know what the patch does - it moves a bunch of stuff to a new includes/install_drupal.inc file and then makes the other necessary small changes for that to work correctly. I'm therefore somewhat tempted to force this issue to stay at RTBC by uploading a tiny dummy patch that does nothing - that way the testbot won't set it back, and we can actually reroll the real patch once more, when it's actually going to be committed.....

JacobSingh’s picture

Something has broken here recently I think, still tracking it down.

As a side note, when investigating, I saw this:

// The user agent header is used to pass a database prefix in the request when
// running tests. However, for security reasons, it is imperative that no
// installation be permitted using such a prefix.
if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], "simpletest") !== FALSE) {
header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
exit;
}
I don't see how this protects anything. I can easily change my user agent to simpletest, and since it is in Drupal core, it's not like it is a secret or anything.

-J

JacobSingh’s picture

Ah... I just up'd to snow leopard which uses php 5.3.0, and command line install doesn't work from there.

I don't know exactly what the issue is, but the output is something about PDO not getting what it is looking for in terms of a driver name.

If someone is interested / has a quick idea:

Warning: PDO::__construct(): [2002] No such file or directory (trying to connect via unix:///tmp/mysql.sock) in /Users/jacob/work/gardens-trunk/docroot/includes/database/database.inc on line 324

Warning: Parameter 2 to install_settings_form() expected to be a reference, value given in /Users/jacob/work/gardens-trunk/docroot/includes/form.inc on line 474

Notice: Undefined index: _database in /Users/jacob/work/gardens-trunk/docroot/install.php on line 925

Warning: array_shift() expects parameter 1 to be array, null given in /Users/jacob/work/gardens-trunk/docroot/includes/form.inc on line 1468

Notice: Undefined index: settings_file in /Users/jacob/work/gardens-trunk/docroot/install.php on line 926

Notice: Undefined index: driver in /Users/jacob/work/gardens-trunk/docroot/install.php on line 949

Notice: Undefined index: driver in /Users/jacob/work/gardens-trunk/docroot/install.php on line 951

Fatal error: Uncaught exception 'Exception' with message 'In your <em></em> file you have configured Vineyard to use a <em></em> server, however your PHP installation currently does not support this database type.' in /Users/jacob/work/gardens-trunk/docroot/install.php:416
boombatower’s picture

The reason for the check being so simple in that location is bootstraps does a proper check...so we just want to know that it is a simpletest user agent at that point.

This again seems to point out that we need a proper separation...the install.php which has UI and that check...and an API that can be called from CLI script and UI.

David_Rothstein’s picture

Jacob, I'm not sure about the PDO error, but the second error you saw sounds exactly like #547846: Drupal installation fails on MAMP & XAMPP due to pass-by-reference error, so either a regression there or something similar occurring for a different reason... (and the errors that occur after that are likely side effects of that one).

JacobSingh’s picture

Okay, here is a new one. This is a regression in PHP 5.2:

#524728: Refactor install.php to allow Drupal to be installed from the command line (committed 2 days ago).

Tries to access a cache before it is created.

PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'dev7_drupal.cache' doesn't exist in /Users/jacob/work/github/drupal/includes/database/database.inc on line 1735

Any ideas how to quickly fix this?

JacobSingh’s picture

sorry, wrong link:

#557542: Cache module_implements()

@catch and I looked at this a bit in IRC, and here is the gist:

During system_requirements check in install_drupal, module_implements gets called. That tries to hit the cache. Normally, we would use the FakeCache

#576096: Port cache-install.inc to the new cache system

However, it is made to unset itself after the DB is setup (when doing a GUI install) and start using the DB cache.

When installing from the GUI, it happens over multiple page loads, so this works.

From the CLI, it don't. The cache_default_cache variable gets unset before we have set up the DB, so it fails for us. By commenting out the line to unset the var it works fine.

here is the code for reference:

install.php:277

if ($install_state['settings_verified']) {
    // Since we have a database connection, we use the normal cache system.
    // This is important, as the installer calls into the Drupal system for
    // the clean URL checks, so we should maintain the cache properly.
    unset($conf['cache_default_class']);

    // Initialize the database system. Note that the connection
    // won't be initialized until it is actually requested.
    require_once DRUPAL_ROOT . '/includes/database/database.inc';

    // Verify the last completed task in the database, if there is one.
    $task = install_verify_completed_task();
  }

@catch found this in CVS:
#151280: After install, old cached variables are used instead of those chosen using installer

Where the comment was added about "the cache properly"

Gabor, et all: Is this still required?

David_Rothstein’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new0 bytes

Nice debugging work, Jacob. I experienced a similar problem for a few days (although it magically seems to have gone away for me now!). Anyway, let's continue discussion of that bug at #557542: Cache module_implements() since that is the issue that introduced it.

Here, I'd really like to get this issue closed, and it is still open because we still haven't moved the code out of install.php. So to keep this at RTBC, I'm doing what I suggested above and attaching a patch which will hopefully fool the testbot :) The actual patch that is RTBC is #77 which I will reroll to chase HEAD when asked to do so - i.e., shortly before it is actually going to be committed.

As hinted at above, I don't see how we can ever have a followup issue to let SimpleTest be able to test the installer until this patch is committed (an important goal before releasing Drupal 7, IMO).... The reason is that SimpleTest runs in your web browser, so with the current code that's in core, I don't see how it can include install.php and use parts of it as an API without automatically triggering the code execution.

moshe weitzman’s picture

Priority: Normal » Critical
mcrittenden’s picture

Sub.

dixon_’s picture

subscribing

seutje’s picture

subscribe

sun’s picture

StatusFileSize
new145.39 KB

+1, we absolutely want to do this.

Re-rolled from scratch against HEAD.

Status: Reviewed & tested by the community » Needs work

The last submitted patch failed testing.

sun’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new145.44 KB

Missed the line that includes install.inc.

David_Rothstein’s picture

Thanks for the reroll! I think the name "install.wizard.inc" was already rejected above (so we might rather want something like "install.drupal.inc" or "install.core.inc") - however there is no need to bikeshed because this filename can easily be changed by doing a find and replace on the patch file.... the important thing is that we get this in.

I've gone ahead and preemptively created #630446: Allow SimpleTest to test the non-interactive installer and then marked it postponed on this issue.

sun’s picture

StatusFileSize
new145.44 KB

oh I like install.core.inc! - that makes a clear difference from install.inc and install.core.inc.

webchick’s picture

It's not clear to me exactly what Dries's previous concerns were with this patch, nor why it's required for Drush (drush installcore works fine for me without it). But, since it was ready before code freeze and in any case is just moving some code around, and since it brings install.php inline with similar changes we made with update.php, I'm ok with committing this.

I'll leave it RTBC for another 48 hours for feedback.

David_Rothstein’s picture

It's been more than 48 hours :) But darn - the patch no longer seems to apply... Will you commit it if I reroll it?

I guess it's not strictly-speaking needed for Drush, but is for other issues, such as the one I linked to in #97. Basically, if anyone ever wants to use any of these installer API functions in code that is intended to run in a web browser - for any purpose - they need this patch in order to do so.

scor’s picture

+++ includes/install.core.inc
@@ -0,0 +1,1728 @@
+    include_once DRUPAL_ROOT . '/' . $profile->uri;
+····
+    $details = install_profile_info($profile->name);

The last patch contains trailing whitespaces in several locations.

This review is powered by Dreditor with whitespace detection enabled.

David_Rothstein’s picture

Hm, I actually do not see the trailing whitespace in those places? But even if it's there, this patch is basically just a straight copy-paste of whatever the existing code was, so I don't think we need to fix the whitespace here.

I'm a bit surprised the testbot hasn't found reason to change the RTBC status in the past month... but anyway, it still needs to go in :)

David_Rothstein’s picture

StatusFileSize
new146.83 KB

Rerolled the patch.

David_Rothstein’s picture

StatusFileSize
new1.08 KB
new761 bytes

On the off chance that this is not the last time it will need to be rerolled, I've also attached helper patches for rerolling it again. Instructions:

  1. Cut and paste everything in install.php starting with:
    /**
     * Global flag to indicate that a task should not be run during the current
     * installation request.
    

    near the top of the file, and ending with:

      // Record when this install ran.
      variable_set('install_time', $_SERVER['REQUEST_TIME']);
    }
    

    near the bottom of the file. Put this in a new file called includes/install.core.inc. Make sure that the remaining code in install.php has the standard spacing.

  2. Apply the installcore.helper.1.txt patch file (attached).
  3. Apply the installcore.helper.2.txt patch file (attached).

That should do it.

int’s picture

Last Post "2 weeks 3 days", what is needed here? Commit?

Status: Reviewed & tested by the community » Needs review

Re-test of install.core_.524728.103.patch from comment #103 was requested by David_Rothstein.

Status: Needs review » Needs work

The last submitted patch, install.core_.524728.103.patch, failed testing.

David_Rothstein’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new147.18 KB

Rerolled using the above instructions. RTBC because it's been that way for months.

Actually, this is kind of amusing. The initial patch in this issue, which basically involved a complete rewrite of the entire Drupal installer, took a week and a half to get in. The followup patch for cutting and pasting code from one file to another - this one's at five and a half months and counting :)

webchick’s picture

#110: install.core_.524728.110.patch queued for re-testing.

(Edit: Oooooh. I like the new way this feature works!)

Status: Reviewed & tested by the community » Needs work

The last submitted patch, install.core_.524728.110.patch, failed testing.

anarcat’s picture

Status: Needs work » Needs review

#110: install.core_.524728.110.patch queued for re-testing.

I assume the test failure were unrelated with this patch, as they were about some minor javascript generation problem while all other tests were succeeding, requeuing.

sun.core’s picture

Status: Needs review » Reviewed & tested by the community

We are still waiting :)

moshe weitzman’s picture

StatusFileSize
new147.18 KB

Re-post latest patch to awaken bot.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, tmp.diff, failed testing.

David_Rothstein’s picture

Status: Needs work » Needs review
StatusFileSize
new147.29 KB

Rerolled.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

Wait for green before commit (WFGBC)

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Let's do this. It's been ready since forever, it makes this consistent with what we did for update.php, and frankly I'm getting sick and tired of this haunting me in my RTBC queue. ;P

And though the issue status doesn't reflect it atm, qa.drupal.org shows this gets the green light.

Committed to HEAD. Dries, if you strongly disagree, feel free to roll back.

Status: Fixed » Closed (fixed)

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

christefano’s picture

Is this issue summary the only place where this is documented? It's 4 years later but I don't see anything about this in INSTALL.txt or at https://drupal.org/documentation/install/run-script