Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 46 additions & 14 deletions lib/plugins/TaskRunner/Drupal.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class Drupal extends LAMPApp {
* @param {object} options - A hash of configuration options specific to this task.
* @param {boolean} options.clearCaches - Whether to clear all caches after the build is finished. Defaults to true.
* @param {string} options.siteFolder - The site folder to use for this build (the folder within the drupal `sites` folder). Defaults to `default`.
* @param {string} options.database - The name of the database to import if specified. Note that this database *must be added to the assets array separately*.
* @param {string} options.databaseName - The name of the database to use. Defaults to 'drupal'.
* @param {string} options.database - The filename of the database to import if specified. Note that this database *must be added to the assets array separately*.
* @param {boolean} options.databaseGzipped - Whether the database was sent gzipped and whether it should therefore be gunzipped before importing.
* @param {boolean} options.databaseBzipped - Whether the database was sent bzipped and whether it should therefore be bunzipped before importing.
* @param {boolean} options.databaseUpdates - Determines whether to run `drush updb`.
Expand All @@ -26,6 +27,10 @@ class Drupal extends LAMPApp {
* @param {string} options.installArgs - A set of params to concat onto the drush `site-install` command (defaults to '').
* @param {string} options.subDirectory - The directory of the actual web root (defaults to 'docroot').
* @param {string} options.configSyncDirectory - The config sync directory used in Drupal 8.
* @param {string} options.alias - A multisite alias to be used for this site (ex 'example.com').
* @param {string} options.aliasSubdomain - A multisite alias subdomain to the probo build domain (ex 'example'). Defaults to options.alias.
* @param {string} options.databasePrefix - A string to prefix all database tables.
* @param {boolean} options.skipSetup - A boolean to indicate that we can skip the server setup. You probably shouldn't be setting this yourself, instead it should be passed from the multisite app.
* @param {string} [options.settingsAppend] - A snippet to append to the end of the settings.php file.
* @param {string} [options.settingsRequireFile] - A file to require at the end of settings.php (in order to get around not
* checking settings.php into your repo).
Expand All @@ -34,10 +39,11 @@ class Drupal extends LAMPApp {
constructor(container, options) {
super(container, options);

this.databaseName = 'drupal';
this.databaseName = options.databaseName || 'drupal';

this.options.siteFolder = options.siteFolder || 'default';
this.options.profileName = options.profileName || 'standard';

// clearCaches must be set to explicitly false
this.options.clearCaches = (options.clearCaches || typeof options.clearCaches === 'undefined');
this.options.drupalVersion = options.drupalVersion || constants.DEFAULT_DRUPAL_VERSION;
Expand Down Expand Up @@ -70,12 +76,14 @@ class Drupal extends LAMPApp {
*
*/
populateScriptArray() {
this.addScriptSetup();
if (this.options.makeFile) {
this.addScriptRunMakeFile();
}
else {
this.addScriptSymlinks();
if (!this.options.skipSetup) {
this.addScriptSetup();
if (this.options.makeFile) {
this.addScriptRunMakeFile();
}
else {
this.addScriptSymlinks();
}
}
this.addScriptCreateDatbase();
this.addScriptAppendSettingsPHPSettings();
Expand All @@ -98,8 +106,9 @@ class Drupal extends LAMPApp {
if (this.options.clearCaches) {
this.addScriptClearCaches();
}

this.addScriptApachePhp();
if (!this.options.skipSetup) {
this.addScriptApachePhp();
}
}

addScriptAppendSettingsPHPSettings() {
Expand All @@ -118,7 +127,7 @@ class Drupal extends LAMPApp {
'\\$databases = array(',
' \'default\' => array(',
' \'default\' => array(',
' \'database\' => \'drupal\',',
` \'database\' => \'${this.databaseName}\',`,
' \'username\' => \'root\',',
' \'password\' => \'strongpassword\',',
' \'host\' => \'localhost\',',
Expand All @@ -145,7 +154,7 @@ class Drupal extends LAMPApp {
`\\$databases = array(`,
` 'default' => array(`,
` 'default' => array(`,
` 'database' => 'drupal',`,
` 'database' => '${this.databaseName}',`,
` 'username' => 'root',`,
` 'password' => 'strongpassword',`,
` 'host' => 'localhost',`,
Expand All @@ -169,12 +178,35 @@ class Drupal extends LAMPApp {
}

appendCustomSettings() {
var settings_path = `/var/www/html/sites/${this.options.siteFolder}/settings.php`;

if (this.options.alias) {
var alias;
var subdomain;
var build_protocol;
var build_domain;
var fqdn;

alias = this.options.alias;
subdomain = this.options.aliasSubdomain || alias;
build_protocol = this.container.build.links.build.split('://')[0];
build_domain = this.container.build.links.build.split('://')[1];
fqdn = build_protocol + '://' + subdomain + '.' + build_domain;

this.script.push(`echo "\$sites[${fqdn}] = ${alias};" >> ${settings_path}`);
}

if (this.options.databasePrefix) {
this.script.push(`echo "\$db_prefix = '${this.options.databasePrefix}';" >> ${settings_path}`);
}

if (this.options.settingsRequireFile) {
let command = 'echo "require_once(\'' + this.options.settingsRequireFile + '\');" >> /var/www/html/sites/' + this.options.siteFolder + '/settings.php';
let command = `echo "require_once(\'' + this.options.settingsRequireFile + '\');" >> ${settings_path}`;
this.script.push(command);
}

if (this.options.settingsAppend) {
let command = 'echo ' + shellEscape([this.options.settingsAppend]) + ' >> /var/www/html/sites/' + this.options.siteFolder + '/settings.php';
let command = 'echo ' + shellEscape([this.options.settingsAppend]) + ` >> ${settings_path}`;
this.script.push(command);
}
}
Expand Down
77 changes: 77 additions & 0 deletions lib/plugins/TaskRunner/DrupalMultisite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
'use strict';
var shellEscape = require('shell-escape');
var crypto = require('crypto');

var constants = require('./constants');

var Drupal = require('./Drupal');
var LAMPApp = require('./LAMPApp');

class DrupalMultisite extends LAMPApp {

/**
* Options (used by this task in addition to the Drupal options):
* @param {array} options.sites - A hash of sites. Each site supports the same options as the Drupal plugin, except for @TODO.
* @augments Drupal
*/
constructor(container, options) {
super(container, options);

this.script = [];
this.populateScriptArray();
this.setScript(this.script);
}

description() {
return `${this.plugin} 'Provisioning Drupal Multisite!'`;
}

populateScriptArray() {
this.addScriptSetup();
if (this.options.makeFile) {
this.addScriptRunMakeFile();
}
else {
this.addScriptSymlinks();
}

/**
* Loop through each site, merging the plugin options with the site options.
* Virtualize the script output from a Drupal app (skipping the setup), and
* add that to our script. Track which databases have already been imported
* and only import each one once.
*/
for (var site in this.options.sites) {
if (this.options.sites.hasOwnProperty(site)) {
var drupal;
var importedDatabases = [];
var site_options = this.options.sites[site];
site_options.siteFolder = this.options.siteFolder || site;
site_options.alias = site;
site_options.virtualize = true;
site_options.skipSetup = true;
site_options = Object.assign({}, this.options, site_options);

if (site_options.database) {
if (importedDatabases.indexOf(site_options.database) != -1) {
site_options.skipDatabase = true;
}
importedDatabases.push(site_options.database);
}

drupal = new Drupal(this.container, site_options);
this.script = this.script.concat(drupal.script);
}
}
this.addScriptApachePhp();
}

// @TODO - this is going to cause problems someday. We should make a Drupal-like
// Plugin that extends LAMPApp, and then Drupal and DrupalMultisite can extend that.
addScriptRunMakeFile() {
this.script.push('cd $SRC_DIR ; drush make ' + this.options.makeFile + ' /var/www/html --force-complete');
this.script.push('rsync -a $SRC_DIR/ /var/www/html/profiles/' + this.options.profileName);
}
}

module.exports = DrupalMultisite;
17 changes: 11 additions & 6 deletions lib/plugins/TaskRunner/Script.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@ var combine = require('stream-combiner');

module.exports = class Script extends require('./AbstractPlugin') {

// requires options:
// - script: string to pipe to the container on stdin or an array of strings
// - secrets: Array of secret strings that need to be filtered out of output
/**
* Options (used by this task):
* @param {object} options.script - String to pipe to the container on stdin or an array of strings.
* @param {array} options.secrets - Array of secret strings that need to be filtered out of output.
* @param {boolean} options.virtualize - If set to true, will build the script but will not run it.
*/
constructor(container, options) {
super(container, options);
options.tty = false;

this.setScript(options.script || '');

var self = this;
this.on('running', function() {
self.runScript();
});
if (!options.virtualize) {
this.on('running', function() {
self.runScript();
});
}
}

setScript(script) {
Expand Down
11 changes: 11 additions & 0 deletions test/tasks/DrupalApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ describe('Drupal App', function() {
database: 'my-cool-db.sql',
databaseGzipped: true,
clearCaches: false,
databaseName: 'cooldb',
databasePrefix: 'prefix',
alias: 'site1.com',
aliasSubdomain: 'siteone',
};
var app2 = new DrupalApp(mockContainer, options2);

Expand All @@ -34,6 +38,7 @@ describe('Drupal App', function() {
app.script.should.containEql('ln -s $SRC_DIR /var/www/html');

app.script.should.containEql('mysql -e \'create database drupal\'');
app2.script.should.containEql('mysql -e \'create database cooldb\'');

app.script.should.containEql(
'cat $ASSET_DIR/my-cool-db.sql | $(mysql -u root --password=strongpassword drupal)'
Expand All @@ -47,6 +52,7 @@ describe('Drupal App', function() {

it('cats the settings.php file', function() {
app.script.should.containEql('\'database\' => \'drupal\'');
app2.script.should.containEql('\'database\' => \'cooldb\'');
app.script.should.containEql('\'username\' => \'root\'');
app.script.should.containEql('\'password\' => \'strongpassword\'');
});
Expand All @@ -55,4 +61,9 @@ describe('Drupal App', function() {
app.script.should.containEql('drush --root=/var/www/html cache-clear all');
app2.script.should.not.containEql('drush --root=/var/www/html cache-clear all');
});

it('handles multisite configuration', function() {
app2.script.should.containEql("$sites[http://siteone.abc123.probo.build] = site1.com;");
app2.script.should.containEql("$db_prefix = 'prefix';");
});
});
84 changes: 84 additions & 0 deletions test/tasks/DrupalMultisiteApp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
'use strict';
var DrupalMultisiteApp = require('../../lib/plugins/TaskRunner/DrupalMultisite');

var mockContainer = {
log: {child: function() {}},
build: {
links: {
build: 'http://abc123.probo.build',
},
},
};


describe('Multisite Drupal App', function() {

// Each site has its own database.
var options = {
sites: {
'site1.com': {
database: 'my-cool-db.sql',
databaseName: 'cooldb',
aliasSubdomain: 'site1',
siteFolder: 'site1folder',
},
'site2.com': {
database: 'my-lame-db.sql',
databaseName: 'lamedb',
aliasSubdomain: 'sitetwo',
},
},
phpConstants: {
multisite: 2,
},

};
var app = new DrupalMultisiteApp(mockContainer, options);

// Each site has a shared database with prefixes.
var options2 = {
sites: {
'site1.com': {
databasePrefix: 'cooldb_prefix',
aliasSubdomain: 'site1',
},
'site2.com': {
databasePrefix: 'lamedb_prefix',
aliasSubdomain: 'sitetwo',
},
},
database: 'my-cool-db.sql',
databaseName: 'cooldb',
};
var app2 = new DrupalMultisiteApp(mockContainer, options2);

it('adds setup commands only once', function() {
(app.script.match(/ln \-s/g) || []).length.should.eql(2);
(app.script.match(/proboPhpConstants/g) || []).length.should.eql(2);
});

it('handles multisite with multiple databases', function() {
app.script.should.containEql('mysql -e \'create database cooldb\'');
app.script.should.containEql('mysql -e \'create database lamedb\'');
app.script.should.containEql('\'database\' => \'cooldb\'');
app.script.should.containEql('\'database\' => \'lamedb\'');
app.script.should.containEql("$sites[http://site1.abc123.probo.build] = site1.com;");
app.script.should.containEql("$sites[http://sitetwo.abc123.probo.build] = site2.com;");
(app.script.match(/lamedb/g) || []).length.should.eql(4);
(app.script.match(/cooldb/g) || []).length.should.eql(4);
});

it('handles multisite with a single shared database', function() {
app2.script.should.containEql('mysql -e \'create database cooldb\'');
app2.script.should.not.containEql('mysql -e \'create database lamedb\'');
app2.script.should.containEql("$db_prefix = 'cooldb_prefix';");
app2.script.should.containEql("$db_prefix = 'lamedb_prefix';");

// Only import the db once
(app.script.match(/my-cool-db\.sql/g) || []).length.should.eql(2);
});

it('should not use the default site folder in multisite repos', function() {
app.script.should.not.containEql("var/www/html/sites/default/settings.php");
});
});