Automate installing drupal on ubuntu for drupal training purpose

Posted by: 
Dominique De Cooman

This post will explain how to automate installing drupal on a development environment using a stack installer script.

We used the script to speed up a drupal training. With the script and the install profile we could set up a development workstation very fast. Leaving us more time to look at the important stuff.

It also gave us a chance to explain the power of drupal distributions. The fact that drupal is capable of being installed as a different product using an install profile gives it an edge over other competing opensource cms. This could be interesting for bigger (enterprise) clients who want to develop products on top of drupal using install profiles. More about distributions and install profiles can be found here (http://drupal.org/node/326175 and to follow http://www.drupaldistrowatch.com)

Installing

We assumed an ubuntu was already installed. If not, installing the operating system is trivial. Go to the ubuntu website (http://www.ubuntu.com/desktop/get-ubuntu/download), download and install it.

The install script configures the LAMP stack for drupal, installs APC, drush, drush make and sets up a database for our drupal. After this we launch the make file which downloads all needed code for our drupal. Now the script will configure the drupal file system, set up the permissions and copy the drupal install profile and resources.

execute the script :

./mbs_drupal_installer.sh drupal_instance_name mysql_existing_power_user mysql_power_user_password

#!/bin/bash
 
#!/bin/bash
 
color_echo()
{
	text_reverse_bold="$(tput rev) $(tput bold)"
	text_normal="$(tput sgr0)"
 
	echo "${text_reverse_bold}$*${text_normal}"
}
 
if [ $UID != 0 ]
then
	echo "This script must be run by root".
	exit 1
fi
 
if [ $# != 3 ]
then
	echo "Usage  : $0 drupal_instance_name mysql_existing_power_user mysql_power_user_password"
	echo "Example: $0 mywebsite root \"\""
	echo "Example: $0 mywebsite mysqlpoweruser secretpassword"
	exit 2
fi
 
home_dir=$(dirname $0)
bin_dir=$home_dir/bin
 
drupal_instance_name=$1
mysql_existing_power_user=$2
mysql_power_user_password=$3
 
 
color_echo "Starting Drupal install..."
 
color_echo "Installing Debian/Ubuntu packages..."
apt-get --yes install apache2 php5 php-pear php5-dev php5-gd mysql-server-5.0 php5-mysql mysql-client wget curl
 
color_echo "Setting up the Apache mod_rewrite for Drupal clean urls..."
a2enmod rewrite
 
color_echo "Setting up the Apache mod_expires for Apache Cache-Control directive..."
a2enmod expires
 
color_echo "Setting up the Apache mod_deflate to save bandwidth..."
a2enmod deflate
sed -i 's|DEFLATE text/html text/plain text/xml|DEFLATE text/html text/plain text/xml text/css text/javascript application/x-javascript|' /etc/apache2/mods-available/deflate.conf
 
 
color_echo "Adding PEAR package: progress bars on upload..."
pecl install uploadprogress
sed -i '/; extension_dir directive above/ a\
extension=uploadprogress.so' /etc/php5/apache2/php.ini
 
color_echo "Installing APC php opcode cache..."
pecl install apc
sed -i '/; extension_dir directive above/ a\
extension=apc.so' /etc/php5/apache2/php.ini
 
 
#sed -i 's/query_cache_limit       = 1M/query_cache_limit       = 1M\
#query_cache_type        = 1/' /etc/mysql/my.cnf
#echo "Reloading mysql..."
#/etc/init.d/mysql force-reload
 
color_echo "Reloading Apache..."
/etc/init.d/apache2 force-reload
 
drush_extract_dir=/opt
drush_install_dir=$drush_extract_dir/drush
color_echo "Installing drush in $drush_install_dir ..."
resources_dir=$home_dir/resources
tar xvf $resources_dir/drush-6.x-3.3.tar.gz -C $drush_extract_dir
cp $resources_dir/Console_Table-1.1.3/Table.php $drush_install_dir/includes/table.inc
 
#Installing drush make
drush_make_extract_dir=~/.drush
mkdir $drush_make_extract_dir
tar xvf $resources_dir/drush_make-6.x-2.2.tar.gz -C $drush_make_extract_dir
 
color_echo "Creating the MySQL database for drupal on localhost ..."
$bin_dir/create_database.sh $drupal_instance_name $mysql_existing_power_user $mysql_power_user_password
 
drupal_path="/var/www/$drupal_instance_name"
color_echo "Installing drupal in $drupal_path ..."
 
color_echo "Executing make file..."
/opt/drush/drush make ./multimediabs.make $drupal_path
 
color_echo "Creating additional Drupal directories and files..."
mkdir $drupal_path/profiles/multimediabs
mkdir $drupal_path/sites/all/themes
mkdir $drupal_path/sites/all/modules/custom
mkdir $drupal_path/sites/all/modules/contrib_patched
touch $drupal_path/sites/all/modules/contrib_patched/patches.txt
mkdir $drupal_path/sites/default/files
mkdir $drupal_path/sites/default/tmp
 
color_echo "Copying Drupal profile and installer translation files..."
cp ./multimediabs.profile $drupal_path/profiles/multimediabs/
cp -R $resources_dir/translations $drupal_path/profiles/multimediabs/
 
color_echo "Copying and completing the Drupal settings file..."
cp $drupal_path/sites/default/default.settings.php $drupal_path/sites/default/settings.php
cat $resources_dir/settings_snippet.php >> $drupal_path/sites/default/settings.php
 
color_echo "Copying jquery.ui to module folder..." 
cp -R $resources_dir/jquery.ui $drupal_path/sites/all/modules/contrib/jquery_ui/jquery.ui
 
color_echo "Setting the work files and directories as writable..." 
chmod 777 $drupal_path/sites/default/files
chmod 777 $drupal_path/sites/default/tmp
chmod 777 $drupal_path/sites/default/settings.php
 
color_echo "Copying the drush config file..."
cp  $resources_dir/drushrc.php $drupal_path/
 
color_echo "Restarting apache..."
apachectl restart
 
#color_echo "Installing xhprof"
 
#pecl download xhprof-0.9.2
#tar -xvf xhprof-0.9.2.tgz -C /var/tmp
#cd /var/tmp/xhprof-0.9.2/extension
#phpize
#./configure
#make
#make install
#make test
 
#cp -R /build/buildd/php5-5.3.3/pear-build-download/xhprof-0.9.2/xhprof_html /var/www/xhprof
#ln -s /build/buildd/php5-5.3.3/pear-build-download/xhprof-0.9.2/xhprof_html /var/www/xhprof
#mkdir /var/tmp/xhprof
#chmod 777 /var/tmp/xhprof
 
color_echo "*) creating an Apache virtual host for $drupal_instance_name with path $drupal_path"
cp $resources_dir/vhost /etc/apache2/sites-available/
sed -i "s/multimediabs/$drupal_instance_name/g" /etc/apache2/sites-available/$drupal_instance_name
 
color_echo
color_echo "To complete the installation you must:"
color_echo
color_echo '*) add the drush command to the PATH:'
color_echo "  export PATH=$drush_install_dir:\$PATH"
color_echo
color_echo '*) Change your error settings in php.ini to : error_reporting = E_ALL & ~E_DEPRECATED & ~E_NOTICE'
color_echo
color_echo "*) Create an entry in /etc/hosts : 127.0.0.1      $drupal_instance_name"
color_echo
color_echo "*) Update the virtual host file /etc/apache2/sites-available/$drupal_instance_name"
color_echo "create a symlink ln -s /etc/apache2/sites-available/$drupal_instance_name /etc/apache2/sites-enabled/$drupal_instance_name"
color_echo "restart apache with : sudo apachectl restart"
color_echo
color_echo "Open your browser, go to http://$drupal_instance_name and start the 'multimediabs' pressflow install profile"
color_echo
color_echo "You can then add code and modules in the Drupal instance directory in $drupal_path ."
#color_echo
#color_echo "Add this to php.ini to make xhprof run extension=xhprof.so and xhprof.output_dir=\"/var/tmp/xhprof\" and restart server"

The make file.

; $Id$
;
; ----------------
; Multimediabs Make file
; ----------------
 
 
; Core version
; ------------
; Each makefile should begin by declaring the core version of Drupal that all
; projects should be compatible with.
 
core = 6.x
 
; API version
; ------------
; Every makefile needs to declare its Drush Make API version. This version of
; drush make uses API version `2`.
 
api = 2
 
; Core project
; ------------
; In order for your makefile to generate a full Drupal site, you must include
; a core project. This is usually Drupal core, but you can also specify
; alternative core projects like Pressflow. Note that makefiles included with
; install profiles *should not* include a core project.
 
; Use Pressflow instead of Drupal core:
projects[pressflow][type] = "core"
projects[pressflow][download][type] = "get"
projects[pressflow][download][url] = "<a href="http://launchpad.net/pressflow/6.x/6.20.97/+download/pressflow-6.20.97.tar.gz"
">http://launchpad.net/pressflow/6.x/6.20.97/+download/pressflow-6.20.97.t...</a>  
 
; Modules
; --------
projects[admin_menu][subdir] = contrib
projects[vertical_tabs][subdir] = contrib
 
projects[cck][subdir] = contrib
projects[filefield][subdir] = contrib
projects[imagefield][subdir] = contrib
projects[date][subdir] = contrib
projects[jquery_ui][subdir] = contrib
projects[imageapi][subdir] = contrib
projects[imagecache][subdir] = contrib
 
projects[views][subdir] = contrib
 
projects[features][subdir] = contrib
projects[diff][subdir] = contrib
 
projects[pathauto][subdir] = contrib
projects[token][subdir] = contrib
 
projects[i18n][subdir] = contrib
projects[l10n_update][subdir] = contrib
projects[l10n_client][subdir] = contrib
 
 
;Development modules
projects[devel][subdir] = contrib
projects[coder][subdir] = contrib
projects[devel_themer][subdir] = contrib
projects[schema][subdir] = contrib
projects[install_profile_api][subdir] = contrib
projects[update_api][subdir] = contrib
projects[module_builder][subdir] = contrib
 
; Themes
; --------
 
 
; Libraries
; ---------

Now you still need to set your drush PATH, error reporting settings in php.ini, an entry in the hosts file and a vhost. This is not done automatic because the dev station may be used for other projects and we do not want to interfere with these settings. Note also that drush and drush make versions are hard coded in the script. This is to make sure that everyone had the same version of everything so we would waste time in the training on tracing version specific problems.

After you have setup all necessary you ll open the browser and go to your drupal site. There you can select this simple install profile which allows you to select the language.

<?php
function multimediabs_profile_modules() {
  return array(
    
// core modules
    
'menu''search''taxonomy''path''update''syslog''comment''locale''dblog',

    
// cck
    
'content''filefield''text''imagefield''date_api''date''date_popup''jquery_ui'

    
// imagecache
    
'imageapi''imageapi_gd''imagecache''imagecache_ui',


    
// pathauto,
    
'pathauto''token',

    
// views
    
'views''views_ui',

    
// admin improvements
    
'admin_menu''vertical_tabs',
    
    
//IPP
    
'features''diff',
    
    
//Languages
    
'i18n''l10n_update''l10n_client'
    
    
// devel tools
    
'coder''schema''install_profile_api''update_api''module_builder',
  );
}

function 
multimediabs_profile_details() {
  return array(
    
'name' => 'Multimediabs pressflow',
    
'description' => 'Installation drupal multimediabs Tours.'
  
);
}

function 
multimediabs_profile_task_list() {
  
$tasks = array();
  
  if (
_l10n_install_language_selected()) {
    
$tasks['l10n-install-batch'] = st('Download and import translations');
  }
   
  return 
$tasks;
}

function 
multimediabs_profile_tasks(&$task$url) {
  global 
$install_locale;
  
  
install_include(multimediabs_profile_modules());
  
  if (
$task == 'profile') {
    
// Perform the default profile install tasks.
    
include_once('profiles/default/default.profile');
    
default_profile_tasks($task$url);
    
    
// administration theme
    
variable_set('admin_theme''garland');
    
variable_set('node_admin_theme'TRUE);

    
// user registration
    
variable_set('user_register'FALSE);

    
// hide all Garland blocks
    
db_query("UPDATE {blocks} SET status = 0 WHERE theme = 'garland'");

    
// image quality
    
variable_set('image_jpeg_quality'100);
    
variable_set('imageapi_jpeg_quality'100);

    
// files
    
variable_set('file_directory_temp''sites/default/tmp');
    
variable_set('file_directory_path''sites/default/files');

    
// date & time
    
variable_set('configurable_timezones'0);

    
variable_set('date_format_short''d/m/Y - H:i');
    
variable_set('date_format_short_custom''d/m/Y - H:i');

    
variable_set('date_format_media''D, d/m/Y - H:i');
    
variable_set('date_format_media_custom''D, d/m/Y - H:i');

    
variable_set('date_format_long''l, j F, Y - H:i');
    
variable_set('date_format_long_custom''l, j F, Y - H:i');

    
// error reporting
    
variable_set('error_level'0);

    
// roles
    
db_query("INSERT INTO {role} (name) VALUES ('%s')"'site administrator');
    
db_query("INSERT INTO {role} (name) VALUES ('%s')"'editor');

    
// pathauto
    
variable_set('pathauto_node_pattern''');
    
variable_set('pathauto_taxonomy_pattern''');
    
variable_set('pathauto_user_pattern''');
    
variable_set('pathauto_ignore_words''');

    
// permissions
    
$admin_permissions = array('access administration menu''administer blocks''use PHP for block visibility''administer menu''access content''administer nodes''revert revisions''view revisions''administer url aliases''create url aliases''search content''use advanced search''access administration pages''access site reports''administer taxonomy''access user profiles''administer permissions''administer users');
    
$editor_permissions = array('access administration menu''administer menu''access content''administer nodes''revert revisions''view revisions''search content''use advanced search''access administration pages');
    
_multimediabs_add_permissions(3$admin_permissions);
    
_multimediabs_add_permissions(4$editor_permissions);

    
// input format permissions
    
db_query("UPDATE {filter_formats} SET roles = ',4,3,' WHERE format IN (2, 3)");

    
// hide module descriptions for admin
    
db_query("UPDATE {users} SET data = '%s' WHERE uid = 1"serialize(array('admin_compact_mode' => TRUE)));

    
// Update the menu router information.
    
menu_rebuild();

    
//Activate devel and set xhprof
    
drupal_install_modules(array('devel'));
    
    
/*
    variable_set('devel_xhprof_enabled', 1);
    variable_set('devel_xhprof_directory', '/build/buildd/php5-5.3.3/pear-build-download/xhprof-0.9.2');
    variable_set('devel_xhprof_url', '<a href="http://localhost/xhprof'">http://localhost/xhprof'</a>);
    */
    
    // Move forward to our install batch.
    
$task 'l10n-install';
  }

  
// Download and import translations if needed.
  
if ($task == 'l10n-install') {
    if (
_l10n_install_language_selected()) {
      
$history l10n_update_get_history();
      
module_load_include('check.inc''l10n_update');
      
$available l10n_update_available_releases();
      
$updates l10n_update_build_updates($history$available);

      
module_load_include('batch.inc''l10n_update');
      
$updates _l10n_update_prepare_updates($updatesNULL, array());
      
$batch l10n_update_batch_multiple($updatesLOCALE_IMPORT_KEEP);

      
// Overwrite batch finish callback, so we can modify install task too.
      
$batch['finished'] = '_l10n_install_batch_finished';

      
// Start a batch, switch to 'l10n-install-batch' task. We need to
      // set the variable here, because batch_process() redirects.
      
variable_set('install_task''l10n-install-batch');
      
batch_set($batch);
      
batch_process($url$url);
    }
  }

  if (
$task == 'l10n-install-batch') {
    include_once 
'includes/batch.inc';
    return 
_batch_page();
  }
}

function 
multimediabs_form_alter(&$form$form_state$form_id) {
  if (
$form_id == 'install_configure') {
    
$form['site_information']['site_name']['#default_value'] = 'MBS';
    
$form['site_information']['site_mail']['#default_value'] = ini_get('sendmail_from') ? ini_get('sendmail_from') : '<a href="mailto:info@orangembs.fr">info@orangembs.fr</a>';
    
$form['admin_account']['account']['name']['#default_value'] = 'admin';
    
$form['admin_account']['account']['mail']['#default_value'] = '<a href="mailto:info@orangembs.fr">info@orangembs.fr</a>';
  }
}

function 
_multimediabsformat_set_roles($roles$format_id) {
  
$roles implode(',',$roles);
  
// Drupal core depends on the list of roles beginning and ending with commas.
  
if (!empty($roles)) {
    
$roles ','$roles .',';
  }
  
db_query("UPDATE {filter_formats} SET roles = '%s' WHERE format = %d"$roles$format_id);
}

function 
_multimediabs_add_permissions($rid$perms) {
  
// Retrieve the currently set permissions.
  
$result db_query("SELECT p.perm FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid WHERE r.rid = %d "$rid);
  
$existing_perms = array();
  while (
$row db_fetch_object($result)) {
    
$existing_perms += explode(', '$row->perm);
  }
  
// If this role already has permissions, merge them with the new permissions being set.
  
if (count($existing_perms) > 0) {
    
$perms array_unique(array_merge($perms, (array)$existing_perms));
  }

  
// Update the permissions.
  
db_query('DELETE FROM {permission} WHERE rid = %d'$rid);
  
db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')"$ridimplode(', '$perms));
}

/**
 * Check whether we are installing in a language other than English.
 */
function _l10n_install_language_selected() {
  global 
$install_locale;
  return !empty(
$install_locale) && ($install_locale != 'en');
}

/**
 * Batch finish callback for l10n_install batches.
 */
function _l10n_install_batch_finished($success$results) {
  if (
$success) {
    
variable_set('install_task''profile-finished');
  }
  
// Invoke default batch finish function too.
  
module_load_include('batch.inc''l10n_update');
  
_l10n_update_batch_finished($success$results);

  
// Set up l10n_client and inform the admin about it.
  // @todo This message will not show up for some reason.
  
global $user;
  
variable_set('l10n_client_use_server'1);
  
variable_set('l10n_client_server''<a href="http://localize.drupal.org'">http://localize.drupal.org'</a>);
  drupal_set_message(t('Localization client is set up to share your translations with <a href="
@localize">localize.drupal.org</a>. Each participating user should have a localize.drupal.org account and set up their API key on their user profile page. <a href="@edit-profile">Set up yours</a>.', array('@localize' => '<a href="http://localize.drupal.org'">http://localize.drupal.org'</a>, '@edit-profile' => url('user/' . $user->uid . '/edit'))));
  
  //Set language defaults
  
variable_set('language_negotiation'1);
  
db_query("UPDATE {variable} SET value = '%s' WHERE name = '%s'"'O:8:"stdClass":11:{s:8:"language";s:2:"en";s:4:"name";s:7:"English";s:6:"native";s:7:"English";s:9:"direction";s:1:"0";s:7:"enabled";i:1;s:7:"plurals";s:1:"0";s:7:"formula";s:0:"";s:6:"domain";s:0:"";s:6:"prefix";s:0:"";s:6:"weight";s:1:"0";s:10:"javascript";s:0:"";}''language_default');
  
  global 
$theme_key;
  
$theme_key 'garland';
  
_block_rehash();
  
install_set_block('user''0''garland''left');
  
install_set_block('locale''0''garland''left');
}
?>

Using this script to automate install is fast but it is not the only solution.

Here are some Other possibilities:

Drubuntu
You can get the script here (http://drupal.org/drubuntu). It works great. It installs lots of things(project page) but there isn't a lot of transparancy there. Meaning unless you read all the code to see what it actually does and where it puts everything, which versions, ... you don't really know whats going on. Note that it does quit a lot to your system, perhaps you dont want certain things but there are no options available.

It has some powerful capabilities though. Besides what you find on the project page, it provides drush commands to set up a site, to create a sandbox. It looks like a great tool but it is not very documented. So for beginners it might be a bit too complicated.

Quickstart
Yet another option is working in a vbox. You have http://drupal.org/project/quickstart to use. It has installed quit a bit (lamp stack, drush, eclipse, netbeans, ...). It is easy to use. When launching the browser you ll see everything you need. It works great but it assumes you ll be working in a vbox. I really like the philosophy behind it not polluting you instance, every project has its own environment and it is easy to share a vbox with other developers. I use it daily for personal development.

Acquia
Ofcourse you have Acquia drupal, which also comes as a stack installer on all platforms. You can get it here (http://network.acquia.com/downloads).

Buildkit
Created by development seed an install profile to create distrubtion and drupal sites. (http://drupal.org/project/buildkit)
It follows the logic as the install profile we build an easy simple setup to get started fast. However this is only an install profile it has no server setup. (Note that it only supports D7.)

Conclusion

A lot has been done around automating drupal installation, you should use it when possible to save yourself and others some time. Pick the one that suits your needs best.

For us drubuntu installed too much and it was a bit too intrusive, quickstart was no option because we had a machine at our disposal that we needed to use, acquia comes with acquia drupal which we didnt want and buildkit was drupal 7 and the training was presented for D6, so we ended up creating this lightweight script that configures the LAMP stack and installs our localisable pressflow drupal.

RESOURCES:The script and its required resources you can get on github. (https://github.com/dominiquedc/drupal-stack-installer-mbs)

NEXT:In the next post I ll report how the training of two days using this installer went.

Add new comment