Skip to content

Instantly share code, notes, and snippets.

@tazeverywhere
Created May 3, 2018 16:31
Show Gist options
  • Save tazeverywhere/74cb96170386bfb91eb140f57ed00ac7 to your computer and use it in GitHub Desktop.
Save tazeverywhere/74cb96170386bfb91eb140f57ed00ac7 to your computer and use it in GitHub Desktop.
Pull.php: Version control your MODX site using git. Pulls changes from git and processes database changes in changesets.
<?php
/**
* Pull.php:
* Version control your MODX site using git.
* Pulls changes from git and processes database changes in changesets.
*
* Authors:
* Jeroen Kenters / www.kenters.com
* Bert Oost / www.oostdesign.com
*
* License:
* GPL
*
* Warning:
* This code is provided as is without any warranty! Use at your own risk!
* Always make backups!
*
* Some documentation:
* http://kenters.com/weblog/2013/12/09/my-modx-git-workflow/
*/
set_time_limit(3600); //1 hour should be enough to process all changes :-)
//Initialize MODX
require_once(dirname(dirname(__FILE__)).'/config.core.php');
require_once MODX_CORE_PATH . 'model/modx/modx.class.php';
$modx= new modX();
$modx->initialize('mgr');
$modx->setLogLevel(modX::LOG_LEVEL_INFO);
$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'FILE');
//Get new stuff from git (don't run when client is local or when told to in $_GET)
if(!isset($_GET['skipPull']) && $_SERVER['REMOTE_ADDR'] != '127.0.0.1') {
$pullResponse = `git pull`;
if(stristr($pullResponse, 'Already up-to-date') !== false) {
doLog('No changes to pull from git repo');
die('Already up-to-date.');
}
}
else {
doLog('Skipping git pull.');
}
//Get database settings
$db_name = $modx->config['dbname'];
$db_user = $modx->config['username'];
$db_pass = $modx->config['password'];
$db_host = $modx->config['host'];
if(empty($db_name) || empty($db_user) || empty($db_pass)) {
doLog('Database name, user and/or password unknown.. Please check it out!');
die();
}
//Dump database file into MODX Core Export path (don't run when client is local or when told to in $_GET)
if(!isset($_GET['skipDump']) && $_SERVER['REMOTE_ADDR'] != '127.0.0.1') {
$exportPath = MODX_CORE_PATH.'export/sql/';
if(!file_exists($exportPath)) {
`mkdir -p $exportPath`;
}
$backupPath = $exportPath.'dump.'.date('YmdHis').'.sql';
`mysqldump -h $db_host --user=$db_user --password=$db_pass $db_name > $backupPath`;
doLog('Dumped database to '.$backupPath);
}
// define changesets path
$changesPath = dirname(__FILE__).'/changesets/';
//Apply changes, starting after previous change
/** @var modSystemSetting $changeIndex */
$changeIdxSetting = $modx->getObject('modSystemSetting', array('key' => 'git.pull_change_index'));
if(empty($changeIdxSetting) || !is_object($changeIdxSetting)) {
$changeIdxSetting = $modx->newObject('modSystemSetting');
$changeIdxSetting->set('key', 'git.pull_change_index');
$changeIdxSetting->set('value', '1');
}
$changeIdx = isset($_GET['resetChangeIdx']) ? (int)$_GET['resetChangeIdx'] : (int) $changeIdxSetting->get('value'); //useful when doing initial dev without other environments like testing/production
while(isChangeSet($changeIdx)) {
//SQL before PHP changes
$pre_sql_file = $changesPath.'/'.$changeIdx.'/pre.sql';
if(file_exists($pre_sql_file)) {
doLog('Running pe-SQL for changeset '.$changeIdx);
$error = `mysql -u $db_user --password=$db_pass -h $db_host $db_name < $pre_sql_file`;
if(!empty($error)) { doLog('pre-SQL Error: '.$error, modX::LOG_LEVEL_ERROR); }
}
//PHP changes (MODX API)
$php_file = $changesPath.'/'.$changeIdx.'/changes.php';
if(file_exists($php_file)) {
doLog('Running PHP changeset '.$changeIdx);
include_once($php_file);
}
//SQL after PHP changes
$post_sql_file = $changesPath.'/'.$changeIdx.'/post.sql';
if(file_exists($post_sql_file)) {
doLog('Running post-SQL for changeset '.$changeIdx);
$error = `mysql -u $db_user --password=$db_pass -h $db_host $db_name < $post_sql_file`;
if(!empty($error)) { doLog('post-SQL Error: '.$error, modX::LOG_LEVEL_ERROR); }
}
//Update index
$changeIdx++;
}
// Store index for next run
$changeIdxSetting->set('value', $changeIdx);
$changeIdxSetting->save();
// Clear cache
doLog('Clearing cache');
$modx->cacheManager->refresh();
$modx->invokeEvent('OnSiteRefresh');
doLog('Finished pulling new changes');
/**
* Check if $idx is an existing changeset (either has a php or sql file)
* @param $idx The set index to check agains
* @return bool
*/
function isChangeSet($idx) {
global $changesPath;
$path = $changesPath.$idx.'/';
if(file_exists($path.'pre.sql') || file_exists($path.'changes.php') || file_exists($path.'post.sql')) {
return true;
}
return false;
}
/**
* modX::log wrapper to set target options once
* @param $level
* @param $msg
*/
function doLog($msg, $level=modX::LOG_LEVEL_INFO) {
global $modx;
$targetOptions = array(
'target' => 'FILE',
'options' => array(
'filepath' => dirname(__FILE__).'/',
'filename' => 'pull.log',
)
);
return $modx->log($level, $msg, $targetOptions);
}
//prevent errors when using APC cache
session_write_close();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment