Commit 7192793d authored by Brian Dukes's avatar Brian Dukes
Browse files

updates from using it over time

parent 764a9007
......@@ -3,4 +3,5 @@ vendor/
/deploy-config/
/ssh/
/Vagrantfile
/.vagrant
\ No newline at end of file
/.vagrant
web/app/modules/projects/sql_dumps
\ No newline at end of file
......@@ -2,55 +2,55 @@
class project extends model {
protected static $_table = 'projects';
protected static $_fill = array('type_id', 'gitlab_id', 'name', 'domain', 'folder');
protected static $_required_fields = array('type_id', 'name', 'domain', 'folder');
public function save() {
$errors = array();
foreach (self::$_required_fields as $field) {
if (empty($this->_data[$field])) {
$errors[] = $field;
}
}
if ($errors) {
return array('success' => 0, 'errFields' => $errors);
} else {
return array('success' => 1, 'id' => parent::save());
}
}
protected static $_table = 'projects';
protected static $_fill = array('type_id', 'gitlab_id', 'name', 'domain', 'folder', 'remote_db_host', 'remote_db_user', 'remote_db_pass', 'remote_db_name', 'remote_db_prefix');
protected static $_required_fields = array('type_id', 'name', 'domain', 'folder', 'remote_db_host', 'remote_db_user', 'remote_db_pass', 'remote_db_name');
public function isSetup() {
//check if repo has been cloned
$this->type = self::$_db->pdoFetch('SELECT * FROM projects_types WHERE id = :type_id', array(':type_id' => $this->type_id));
if (!is_dir($this->type['path'] . '/' . $this->folder)) {
return false;
}
//check if db has been created
// Connect to MySQL
$dbname = $this->folder;
$dbuser = 'root';
$pass = 'root';
$link = mysqli_connect('localhost', $dbuser, $pass);
// Make project the current database
$db_selected = mysqli_select_db($link, $dbname);
if (!$db_selected) {
return false;
public function save() {
$errors = array();
foreach (self::$_required_fields as $field) {
if (empty($this->_data[$field])) {
$errors[] = $field;
}
}
if ($errors) {
return array('success' => 0, 'errFields' => $errors);
} else {
return array('success' => 1, 'id' => parent::save());
}
}
mysqli_close($link);
//check if hosts entry is made
if (gethostbyname($this->domain) != '127.0.0.1') {
return false;
}
//make sure site is set up in apache
if(!file_exists('/etc/apache2/sites-enabled/'.$this->domain.'.conf')){
return false;
public function isSetup() {
//check if repo has been cloned
$this->type = self::$_db->pdoFetch('SELECT * FROM projects_types WHERE id = :type_id', array(':type_id' => $this->type_id));
if (!is_dir($this->type['path'] . '/' . $this->folder)) {
return false;
}
//check if db has been created
// Connect to MySQL
$dbname = $this->folder;
$dbuser = 'root';
$pass = 'root';
$link = mysqli_connect('localhost', $dbuser, $pass);
// Make project the current database
$db_selected = mysqli_select_db($link, $dbname);
if (!$db_selected) {
return false;
}
mysqli_close($link);
//check if hosts entry is made
if (gethostbyname($this->domain) != '127.0.0.1') {
return false;
}
//make sure site is set up in apache
if (!file_exists('/etc/apache2/sites-enabled/' . $this->domain . '.conf')) {
return false;
}
return true;
}
return true;
}
}
......@@ -2,223 +2,263 @@
class project_setup {
protected $setup_steps = array(
'clone_repo' => array('msg' => 'Cloning Repo From GitLab'),
'run_composer' => array('msg' => 'Running Composer Commands (If Necessary)'),
'sync_db' => array('msg' => 'Syncing Database'),
'hosts_entry' => array('msg' => 'Adding entry to hosts file'),
'apache_config' => array('msg' => 'Adding Apache Configuration File and Restarting Apache'),
);
protected $project;
protected static $_db;
protected $_gitlab_api;
public function __construct() {
self::$_db = \db::getInstance('default');
require_once(SYS_ROOT . '/app/code/local/classes/Gitlab/Api.php');
$this->_gitlab_api = new Gitlab_Api();
if (empty($_SESSION['projects']['setup_current_message'])) {
$this->setStatus('Cloning Repo From GitLab');
protected $setup_steps = array(
'clone_repo' => array('msg' => 'Cloning Repo From GitLab'),
'run_composer' => array('msg' => 'Running Composer Commands (If Necessary)'),
'sync_db' => array('msg' => 'Syncing Database'),
'hosts_entry' => array('msg' => 'Adding entry to hosts file'),
'apache_config' => array('msg' => 'Adding Apache Configuration File and Restarting Apache'),
);
protected $project;
protected static $_db;
protected $_gitlab_api;
public function __construct() {
self::$_db = \db::getInstance('default');
require_once(SYS_ROOT . '/app/code/local/classes/Gitlab/Api.php');
$this->_gitlab_api = new Gitlab_Api();
if (empty($_SESSION['projects']['setup_current_message'])) {
$this->setStatus('Cloning Repo From GitLab');
}
}
}
public function runSetup() {
foreach ($this->setup_steps as $step => $step_data) {
$this->setStatus($step_data['msg']);
if (method_exists($this, $step)) {
$this->$step();
}
//sleep(2);
public function runSetup() {
foreach ($this->setup_steps as $step => $step_data) {
$this->setStatus($step_data['msg']);
if (method_exists($this, $step)) {
$this->$step();
}
//sleep(2);
}
}
}
public function getStatus() {
$this->sessionControl();
$status = $_SESSION['projects']['setup_current_message'];
$this->sessionControl(false);
return $status;
}
public function sessionControl($enable = true) {
if ($enable) {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
} else {
session_write_close();
public function getStatus() {
$this->sessionControl();
$status = $_SESSION['projects']['setup_current_message'];
$this->sessionControl(false);
return $status;
}
}
public function setStatus($status) {
$this->sessionControl();
$_SESSION['projects']['setup_current_message'] = $status;
$this->sessionControl(false);
}
public function setProject($project) {
$this->project = $project;
$this->project->type = self::$_db->pdoFetch('SELECT * FROM projects_types WHERE id = :type_id', array(':type_id' => $this->project->type_id));
}
private function clone_repo() {
//if the folder has been created then we can assume its already been cloned
if (!is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
if ($this->project->gitlab_id) {
$gitlab_project = $this->_gitlab_api->getProjectById($this->project->gitlab_id);
$clone_url = $gitlab_project->ssh_url_to_repo;
$this->setStatus("Cloning into '{$this->project->folder}'...");
$clone_result = exec("cd {$this->project->type['path']} && git clone $clone_url {$this->project->folder} 2>&1", $output);
if (is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
//clone successful
$this->setStatus("'{$this->project->name}' Cloned Successfully");
//switch to stable branch
exec("cd {$this->project->type['path']}/{$this->project->folder} && git checkout -b stable 2>&1", $output);
public function sessionControl($enable = true) {
if ($enable) {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
} else {
//clone unsuccessful
$this->setStatus("Failed to Clone '{$this->project->name}'. Check to ensure you have your ssh key set up in gitlab and that apache is running as your user.");
die();
session_write_close();
}
}
} else {
$this->setStatus("Project Already Cloned, continuing with setup");
}
}
private function run_composer() {
if (is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
if (file_exists($this->project->type['path'] . '/' . $this->project->folder . '/composer.json')) {
$this->setStatus("Composer found running composer commands");
switch (strtolower($this->project->type['name'])) {
case 'magento':
$command = 'composer update && composer run-script post-install-cmd -vvv -- --redeploy';
break;
default:
$command = 'composer update';
break;
public function setStatus($status) {
$this->sessionControl();
$_SESSION['projects']['setup_current_message'] = $status;
$this->sessionControl(false);
}
public function setProject($project) {
$this->project = $project;
$this->project->type = self::$_db->pdoFetch('SELECT * FROM projects_types WHERE id = :type_id', array(':type_id' => $this->project->type_id));
}
private function clone_repo() {
//if the folder has been created then we can assume its already been cloned
if (!is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
if ($this->project->gitlab_id) {
$gitlab_project = $this->_gitlab_api->getProjectById($this->project->gitlab_id);
$clone_url = $gitlab_project->ssh_url_to_repo;
$this->setStatus("Cloning into '{$this->project->folder}'...");
$clone_result = exec("cd {$this->project->type['path']} && git clone $clone_url {$this->project->folder} 2>&1", $output);
if (is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
//clone successful
$this->setStatus("'{$this->project->name}' Cloned Successfully");
//switch to stable branch
exec("cd {$this->project->type['path']}/{$this->project->folder} && git checkout -b stable 2>&1", $output);
} else {
//clone unsuccessful
$this->setStatus("Failed to Clone '{$this->project->name}'. Check to ensure you have your ssh key set up in gitlab and that apache is running as your user.");
die();
}
}
} else {
$this->setStatus("Project Already Cloned, continuing with setup");
}
$user = exec('whoami');
putenv("COMPOSER_HOME=/home/$user/.composer");
exec("cd {$this->project->type['path']}/{$this->project->folder} && $command 2>&1", $output);
$this->setStatus("Composer ran Successfully");
} else {
$this->setStatus("Composer not found for this project, coninuing with setup");
}
}
}
private function sync_db() {
if (is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
require_once(SYS_ROOT . '/app/code/local/classes/Mysqldump/Mysqldump.php');
$dumpSettings = array(
'add-drop-table' => true,
'add-drop-database' => true,
'exclude-tables' => array(),
);
if ('no' === 'perform_dump_nodata') {
$dumpSettings['no-data'] = true;
}
//https://github.com/ifsnop/mysqldump-php
$dump = new Ifsnop\Mysqldump\Mysqldump("mysql:host={$this->project->remote_db_host};dbname={$this->project->remote_db_name}", $this->project->remote_db_user, $this->project->remote_db_pass, $dumpSettings);
$dumpfile = SYS_ROOT . '/' . MODULES_FOLDER . '/projects/sql_dumps/' . $this->project->folder . '.sql';
if (file_exists($dumpfile)) {
unlink($dumpfile);
}
$dump->start($dumpfile);
//execute file on local db
//some half-assed security measures, please never ever use this out in the wild
$dbname = $this->project->folder;
$dbuser = 'root';
$pass = 'root';
//first check/create table
// Connect to MySQL
$link = mysql_connect('localhost', $dbuser, $pass);
// Make project the current database
$db_selected = mysql_select_db($dbname, $link);
if (!$db_selected) {
// If we couldn't, then it either doesn't exist, or we can't see it.
$sql = 'CREATE DATABASE ' . $dbname;
if (mysql_query($sql, $link)) {
private function run_composer() {
if (is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
if (file_exists($this->project->type['path'] . '/' . $this->project->folder . '/composer.json')) {
$this->setStatus("Composer found running composer commands");
switch (strtolower($this->project->type['name'])) {
case 'magento':
$command = 'composer update && composer run-script post-install-cmd -vvv -- --redeploy';
break;
default:
$command = 'composer update';
break;
}
$user = exec('whoami');
putenv("COMPOSER_HOME=/home/$user/.composer");
exec("cd {$this->project->type['path']}/{$this->project->folder} && $command 2>&1", $output);
$this->setStatus("Composer ran Successfully");
} else {
$this->setStatus("Composer not found for this project, coninuing with setup");
}
}
}
}
public function resyncDb() {
$this->sync_db(true);
}
private function sync_db($resync = false) {
if (is_dir($this->project->type['path'] . '/' . $this->project->folder) && $this->project->remote_db_host && $this->project->remote_db_name) {
require_once(SYS_ROOT . '/app/code/local/classes/Mysqldump/Mysqldump.php');
$dbname = $this->project->folder;
$dbuser = 'root';
$pass = 'root';
$tp = $this->project->remote_db_prefix;
//first check/create table
// Connect to MySQL
$link = mysql_connect('localhost', $dbuser, $pass);
// Make project the current database
$db_selected = mysql_select_db($dbname, $link);
mysql_close($link);
if (!$db_selected) {
// If we couldn't, then it either doesn't exist, or we can't see it.
$sql = 'CREATE DATABASE ' . $dbname;
exec("mysql -u $dbuser -p{$pass} $dbname < $dumpfile"); //yep it's super sketch
if (mysql_query($sql, $link)) {
}
}
$db = new PDO("mysql:host=localhost;dbname=$dbname", $dbuser, $pass);
switch (strtolower($this->project->type['name'])) {
case 'magento':
$siteurl = 'http://'.$this->project->domain.'/';
$tableprefix = $this->project->remote_db_prefix;
$sql = "UPDATE {$tableprefix}core_config_data SET value = '$siteurl' WHERE path IN ('web/unsecure/base_url','web/secure/base_url');
mysql_close($link);
$dumpSettings = array(
'add-drop-table' => true,
'add-drop-database' => true,
'exclude-tables' => array(),
);
//exploring a new idea here:
//DB Dump Step 1: going to first do a dump with no table exclusions but with no data so should be a very fast dump
//DB Dump Step 2: then im going to perform a dump with the data and exclude all the log tables per project type
//this way first step gets entire db structure so no missing tables and second step gets all important data removing the large log ones
//DB Dump Step 1 (structure)
$dumpSettings['no-data'] = true;
//https://github.com/ifsnop/mysqldump-php
$dump = new Ifsnop\Mysqldump\Mysqldump("mysql:host={$this->project->remote_db_host};dbname={$this->project->remote_db_name}", $this->project->remote_db_user, $this->project->remote_db_pass, $dumpSettings);
$dumpfile = SYS_ROOT . '/' . MODULES_FOLDER . '/projects/sql_dumps/' . $this->project->folder . '_structure.sql';
if (file_exists($dumpfile)) {
unlink($dumpfile);
}
$dump->start($dumpfile);
exec("mysql -u $dbuser -p{$pass} $dbname < $dumpfile"); //yep it's super sketch
//DB Dump Step 2 (data)
$dumpSettings['no-data'] = false;
//table exclusions
switch (strtolower($this->project->type['name'])) {
case 'magento':
$dumpSettings['exclude-tables'] = array("{$tp}log_customer", "{$tp}log_quote", "{$tp}log_summary", "{$tp}log_summary_type", "{$tp}log_url", "{$tp}log_url_info", "{$tp}log_visitor", "{$tp}log_visitor_info", "{$tp}log_visitor_online");
break;
case 'basesite':
$dumpSettings['exclude-tables'] = array("{$tp}log_admin_logins", "{$tp}log_admin_logins_failes", "{$tp}log_admin_page_access", "{$tp}log_cron_detail", "{$tp}log_cron_general", "{$tp}log_cron_module_detail", "{$tp}log_sql_errors", "{$tp}log_url_stats", "{$tp}log_user_logins", "{$tp}log_visitor_agents", "{$tp}log_visitor_referers", "{$tp}log_visitors_daily");
break;
}
//https://github.com/ifsnop/mysqldump-php
$dump = new Ifsnop\Mysqldump\Mysqldump("mysql:host={$this->project->remote_db_host};dbname={$this->project->remote_db_name}", $this->project->remote_db_user, $this->project->remote_db_pass, $dumpSettings);
$dumpfile = SYS_ROOT . '/' . MODULES_FOLDER . '/projects/sql_dumps/' . $this->project->folder . '.sql';
if (file_exists($dumpfile)) {
unlink($dumpfile);
}
$dump->start($dumpfile);
exec("mysql -u $dbuser -p{$pass} $dbname < $dumpfile"); //yep it's super sketch
$db = new PDO("mysql:host=localhost;dbname=$dbname", $dbuser, $pass);
switch (strtolower($this->project->type['name'])) {
case 'magento':
$siteurl = 'http://' . $this->project->domain . '/';
$tableprefix = $this->project->remote_db_prefix;
$sql = "UPDATE {$tableprefix}core_config_data SET value = '$siteurl' WHERE path IN ('web/unsecure/base_url','web/secure/base_url');
SET FOREIGN_KEY_CHECKS=0;
UPDATE {$tableprefix}core_store SET store_id = 0 WHERE code='admin';
UPDATE {$tableprefix}core_store_group SET group_id = 0 WHERE name='Default';
UPDATE {$tableprefix}core_website SET website_id = 0 WHERE code='admin';
UPDATE {$tableprefix}customer_group SET customer_group_id = 0 WHERE customer_group_code='NOT LOGGED IN';
SET FOREIGN_KEY_CHECKS=1;";
// there is no point in guarding against sql injection here
// since the whole point is that you can run any sql commands you want on local db
$db->prepare($sql)->execute();
//add db config file
$config_path = $this->project->type['path'].'/'.$this->project->folder;
if(is_dir($config_path.'/web')){
$config_path .= '/web';
}
$config_path .= '/app/etc/local.xml';
$config = file_get_contents(SYS_ROOT.'/app/modules/projects/configs/magento.xml');
$config = str_replace('{{db_name}}', $dbname, $config);
$config = str_replace('{{db_prefix}}', $this->project->remote_db_prefix, $config);
file_put_contents($config_path, $config);
break;
case 'basesite':
$sql = "UPDATE site_settings SET has_certificate = 0 WHERE id=1;
// there is no point in guarding against sql injection here
// since the whole point is that you can run any sql commands you want on local db
$db->prepare($sql)->execute();
if(!$resync){
//add db config file
$config_path = $this->project->type['path'] . '/' . $this->project->folder;
if (is_dir($config_path . '/web')) {
$config_path .= '/web';
}
$config_path .= '/app/etc/local.xml';
$config = file_get_contents(SYS_ROOT . '/app/modules/projects/configs/magento.xml');
$config = str_replace('{{db_name}}', $dbname, $config);
$config = str_replace('{{db_prefix}}', $this->project->remote_db_prefix, $config);
file_put_contents($config_path, $config);
}
break;
case 'basesite':
$sql = "UPDATE site_settings SET has_certificate = 0 WHERE id=1;
UPDATE site_pages SET enable_ssl = 0";
// there is no point in guarding against sql injection here
// since the whole point is that you can run any sql commands you want on local db
$db->prepare($sql)->execute();
break;
default:
break;
}
// there is no point in guarding against sql injection here
// since the whole point is that you can run any sql commands you want on local db
$db->prepare($sql)->execute();
break;
case 'wordpress':
$tableprefix = $this->project->remote_db_prefix;
$sql = "UPDATE {$tableprefix}options SET option_value = 'a:2:{s:8:\"frontend\";i:0;s:5:\"admin\";b:0;}' WHERE option_name='itsec_ssl';";
// there is no point in guarding against sql injection here
// since the whole point is that you can run any sql commands you want on local db
$db->prepare($sql)->execute();
break;
default:
break;
}
}
}
}
private function hosts_entry() {
if (is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
if (gethostbyname($this->project->domain) != '127.0.0.1') {
exec('printf "127.0.0.1 ' . $this->project->domain . "\n" . '" >> /etc/hosts 2>&1', $output);
}
private function hosts_entry() {
if (is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
if (gethostbyname($this->project->domain) != '127.0.0.1') {
exec('printf "127.0.0.1 ' . $this->project->domain . "\n" . '" >> /etc/hosts 2>&1', $output);
}
}
}
}
private function apache_config() {
if (is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
$document_root = $this->project->type['path'] . '/' . $this->project->folder;
if(is_dir($document_root.'/web')){
$document_root .= '/web';
}
if(is_dir($document_root.'/public')){
$document_root .= '/public';
}
$user = exec('whoami');
$virtualHost = "<VirtualHost {$this->project->domain}:80>\nDocumentRoot $document_root\n</VirtualHost>";
exec("sudo -S bash -c 'printf \"$virtualHost\" > /etc/apache2/sites-available/{$this->project->domain}.conf' < /home/$user/.sudopass/sudopass.secret", $output);
exec("sudo -S a2ensite {$this->project->domain} < /home/$user/.sudopass/sudopass.secret", $output);
shell_exec("sudo -S service apache2 reload < /home/$user/.sudopass/sudopass.secret");
$this->setStatus("Setup Finished!");
private function apache_config() {
if (is_dir($this->project->type['path'] . '/' . $this->project->folder)) {
$document_root = $this->project->type['path'] . '/' . $this->project->folder;
if (is_dir($document_root . '/web')) {
$document_root .= '/web';
}
if (is_dir($document_root . '/public')) {
$document_root .= '/public';
}
$user = exec('whoami');
$virtualHost = "<VirtualHost {$this->project->domain}:80>\nDocumentRoot $document_root\n</VirtualHost>";
exec("sudo -S bash -c 'printf \"$virtualHost\" > /etc/apache2/sites-available/{$this->project->domain}.conf' < /home/$user/.sudopass/sudopass.secret", $output);
exec("sudo -S a2ensite {$this->project->domain} < /home/$user/.sudopass/sudopass.secret", $output);
shell_exec("sudo -S service apache2 reload < /home/$user/.sudopass/sudopass.secret");
$this->setStatus("Setup Finished!");
}
}
}
}