Convert your old project to an Installation profile

Before starting with this post, you should have a look at the previous post: Drupal projects architecture.

On the previous post, we have seen another way to organize our projects and to work with Drupal. But, what happens if we already have a site structured in a different way? This post will cover all the different steps we have to follow to re-structure the project into our new architecture.

1.- Create the installation profile

Creating the installation profile it's easy, pick a name for it and create the folder under the profiles folder. Then, create the three files we will be using. With the profile name you choose, create the [profile].info, [profile].profile and [profile].install. Make sure that you don't use a file name of a module that already exists!

The info file is mostly like any other module or theme info file. The main difference is that you need to put there as dependencies the modules you are using in your site. An easy way to get this list is using the "drush pm-list" command. I recommend to group the components you add as dependencies using the same structure you use for your modules folder: contrib, features, custom...

Here is a sample of how it looks the file for my profile called "my_profile":

my_profile.info

name = "My Profile"
description = "My custom installation profile"
core = 7.x
distribution_name = my_profile

; Core dependencies.
dependencies[] = block
dependencies[] = comment
dependencies[] = contextual
dependencies[] = image
dependencies[] = list
dependencies[] = menu
dependencies[] = number
dependencies[] = options
dependencies[] = path
dependencies[] = taxonomy
dependencies[] = dblog
dependencies[] = field_ui
dependencies[] = file

; Contrib modules.
dependencies[] = ctools
dependencies[] = features
dependencies[] = views

; Features.
dependencies[] = my_profile_field_base
dependencies[] = my_profile_field_instance
dependencies[] = my_profile_node
dependencies[] = my_profile_views_view

The .profile file is not used in this approach, but is required for Drupal, or the profile will not work, so we create it with the next content:

my_profile.profile

<?php

/**
 * @file
 * Main profile file for the my_profile installation profile.
 */

The .install file will contain all the hook_install and hook_update functions, but we will fill this later, so we create the file with the minimum content required in order to make it work:

<?php

/**
 * @file
 * This file will contain all the installation and update hooks for the my_profile installation profile.
 */

With only this content we can enable the installation profile and will enable by default all the modules we have specified on the info file. But, we still haven't exported the features and we still don't have any method to download all the contrib code.

With that done, we can already push it to our repository.

2.- Generating make files

With the make file we will have a way to download all the contrib code: modules, themes even the Drupal Core! It will help us to handle the patches and to have a full control of the updates of our contrib code.

The first step is to create our make file for the Drupal core make file:

drupal-org-core.make

api = 2
core = 7.x
projects[drupal][version] = 7.33

; Patches for Core
; projects[drupal][patch][] = "";

Next is the file for our custom profile:

my_profile.make

api = 2
core = 7.x

includes = drupal-org.make

; Custom profile
projects[my_profile][type] = "profile"
projects[my_profile][download][type] = "git"
projects[my_profile][download][url] = "git@github.com:DavidHernandez/profile-demo.git"
projects[my_profile][download][branch] = "master"

And the last make will contain all the custom code. To get a general idea of the contrib modules we are using we can use "drush pm-list" command or a better solution: "drush make-generate drupal-org.make". This last command will create a make file with all of our code (contrib, custom, core...). But, as we already have part of it covered, we will have to edit it. At the end, it should look similar to this:

my_profile.make

; Drupal.org releases file
core = 7.x
api = 2

; Defaults
defaults[projects][subdir] = "contrib"

; Module
projects[ctools][version] = 1.4
projects[features][version] = 2.2
projects[views][version] = 3.8

; Themes
; projects[omega][version] = 4.3

; Libraries
; libraries[jquery_ui][download][type] = "file"
; libraries[jquery_ui][download][url] = "http://jquery-ui.googlecode.com/files/jquery.ui-1.6.zip"
; libraries[jquery_ui][download][md5] = "c177d38bc7af59d696b2efd7dda5c605"

3.- Features

The third step is to convert the features, if we have them into the new component-layered system and if we don't have them, we will create them. As explained on the previous, we use features to export the configuration to code, so we can replicate the same functionality as in the production environment without the need of importing a DB backup.

If you already have some features, you need to disable them. That should not delete any configuration, as you already have it on the database. After that, move the features out of the Drupal installation (or remove them, but keep a backup!). When it's done, the site should be in the same state as a project without exported features.

To create the features, you can try this script included in this GitHub repository. You should have your installation profile in the same Drupal installation of the site you are converting. Note that the script might need some fixes in order to work and you might need to add new components. If you have to add a new component, make sure to put it in the correct order: after any other component that might be a dependency.

If you don't use the script, you can do it manually or with Drush. Basically, you have to create a new feature for each component type (field_bases, field_instances, views_view...) and you have to do it in the correct order to avoid having different component types in the same feature (this might cause conflicts and other problems later). To know the correct order, try to create the feature, if it's autoselecting other components by default, you should export those components before the current feature. And be sure to enable the feature once is created, so it can be picked as dependency for the next features.

Last advices: maybe you don't need to export the permissions to a feature, as some modules might be different between the environments. To export the system variables used by a lot of modules to store their configuration, use the Strongarm module. But don't automate the creation of the variables feature! Do it manually, so you don't export timestamps, versions or other things that will be overridden most of the time.

4.- Review the rest of the configurations

Now we can test our installation profile, do it in a clear environment, don't override the current intallation. Review the functionality of the site to see that everything works in the same way as the original site. Test as much functionality without content as possible, to detect where the content is tied to the functionality. Those parts would be a good point for some refactorization. Then, create some test content to see how it works.

When you find something that doesn't work and is not related with content, but by a module, you will have to review how the module stores the configuration and then, implement functions on your my_profile.install to create this default configuration.

If some functionality doesn't work because requires some code, you can use something like node_export or menu_import to import the content you need. But keep in mind that needing this content in the code or configuration, usually means that that piece of content should not be content.

Keep adding the missing functionality to the my_profile.install file until you think everything is covered and that you can replicate the original site (without the content), just installing the site.

5.- Move the existing site to use the profile

If you have reached this point, it means you are ready to move the old site to the new profile system. If your site is in a live environment, test this deeply out of the live environment. Once you are happy, set the live environment to maintenance and proceed to replicate the changes. Make sure that you take backups of database and code!

Here is the procedure to follow:

  1. Create a copy of the site structure without installing it.
  2. Copy the settings.php file to the new installation.
  3. Copy the files folder (or even better, link it!) to the correct sites folder (probably sites/default/files).
  4. Update the system table on the database to tell Drupal that is using your profile:
    UPDATE system SET name='my_profile', filename='profiles/my_profile/my_profile.profile' WHERE name = 'standard';
    You will have to update this query to use your real profile name and to change standard to minimal, depending of the installation profile you used when installing the site for the first time.
  5. Update 09/02/2015 from jonhattan: The profile name is also stored on a system variable called install_profile. You have to update it too. You can do it with "drush vset install_profile 'my_profile'".
  6. Rebuild the registry with registry_rebuild: drush rr --fire-bazooka
  7. Clear the cache: drush cc all
  8. Revert features: drush fra -y
  9. Run updates: drush updb -y
  10. Test it!
  11. Enable the new installation (or replace the old one).
  12. Disable the maintenance mode.

After this, your site should be running on the new installation profile! Test carefully all of this, as it's not an easy process and some parts are highly experimental. Be sure to have backups, just in case something goes wrong.