Index: branches/5.3.x/core/units/helpers/deployment_helper.php =================================================================== diff -u -N -r15928 -r15956 --- branches/5.3.x/core/units/helpers/deployment_helper.php (.../deployment_helper.php) (revision 15928) +++ branches/5.3.x/core/units/helpers/deployment_helper.php (.../deployment_helper.php) (revision 15956) @@ -1,6 +1,6 @@ _event = $event; } /** - * Adds message to script execution log + * Adds message to script execution log. * - * @param string $message - * @param bool $new_line - * @return void - * @access private + * @param string $message Message. + * @param boolean $new_line Jump to next line. + * + * @return string */ private function toLog($message, $new_line = true) { - $log_file = (defined('RESTRICTED') ? RESTRICTED : WRITEABLE) . '/project_upgrades.log'; + if ( $new_line ) { + $message .= PHP_EOL; + } - $fp = fopen($log_file, 'a'); - fwrite($fp, $message . ($new_line ? "\n" : '')); - fclose($fp); + $this->logData['Output'] .= $message; - chmod($log_file, 0666); + return $message; } /** - * Loads already applied revisions list of current module + * Loads already applied revisions list of current module. * - * @return void - * @access private + * @return self */ private function loadAppliedRevisions() { - $sql = 'SELECT AppliedDBRevisions - FROM ' . TABLE_PREFIX . 'Modules - WHERE Name = ' . $this->Conn->qstr($this->moduleName); - $revisions = $this->Conn->GetOne($sql); + $sql = 'SELECT RevisionNumber + FROM ' . TABLE_PREFIX . 'ModuleDeploymentLog + WHERE Module = ' . $this->Conn->qstr($this->moduleName); + $this->appliedRevisions = array_flip($this->Conn->GetCol($sql)); - $this->appliedRevisions = $revisions ? explode(',', $revisions) : Array (); + return $this; } /** - * Saves applied revision numbers to current module record + * Deploys changes from all installed modules. * - * @return void - * @access private - */ - private function saveAppliedRevisions() - { - // maybe optimize - sort($this->appliedRevisions); - - $fields_hash = Array ( - 'AppliedDBRevisions' => implode(',', $this->appliedRevisions), - ); - - $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Modules', '`Name` = ' . $this->Conn->qstr($this->moduleName)); - } - - /** - * Deploys changes from all installed modules + * @param boolean $dry_run Use dry run mode? * - * @param bool $dry_run - * @return bool - * @access public + * @return boolean */ public function deployAll($dry_run = false) { @@ -188,9 +173,7 @@ $ret = true; $this->dryRun = $dry_run; - $this->toLog(PHP_EOL . '[' . date('Y-m-d H:i:s') . '] === ' . $this->ip . ' ==='); - - foreach ($this->Application->ModuleInfo as $module_name => $module_info) { + foreach ( $this->Application->ModuleInfo as $module_name => $module_info ) { $this->moduleName = $module_name; if ( !file_exists($this->getModuleFile('project_upgrades.sql')) ) { @@ -214,10 +197,9 @@ } /** - * Runs user-specific shell script when deployment happens from Web + * Runs user-specific shell script when deployment happens from Web. * * @return string - * @access protected */ protected function _runShellScript() { @@ -226,7 +208,7 @@ } $wrapper_script = '/usr/local/bin/guest2host_server.sh'; - $script_name = FULL_PATH .'/tools/' . ($this->dryRun ? 'synchronize.sh' : 'deploy.sh'); + $script_name = FULL_PATH . '/tools/' . ($this->dryRun ? 'synchronize.sh' : 'deploy.sh'); if ( file_exists($wrapper_script) && file_exists($script_name) ) { $script_name = preg_replace('/^.*\/web/', constant('DBG_LOCAL_BASE_PATH'), $script_name); @@ -238,11 +220,11 @@ } /** - * Deploys pending changes to a site + * Deploys pending changes to a site. * - * @param string $module_name - * @return bool - * @access private + * @param string $module_name Module name. + * + * @return boolean */ private function deploy($module_name) { @@ -265,10 +247,9 @@ } /** - * Import latest languagepack (without overwrite) + * Import latest language pack (without overwrite). * - * @return void - * @access private + * @return self */ private function importLanguagePack() { @@ -279,13 +260,14 @@ $filename = $this->getModuleFile('english.lang'); $language_import_helper->performImport($filename, '|0|1|2|', $this->moduleName); $this->displayStatus('OK'); + + return $this; } /** - * Exports latest language pack + * Exports latest language pack. * - * @return void - * @access private + * @return self */ private function exportLanguagePack() { @@ -302,13 +284,14 @@ /* @var $language_import_helper LanguageImportHelper */ $language_import_helper->performExport(EXPORT_PATH . '/' . $this->moduleName . '.lang', '|0|1|2|', $languages, '|' . $this->moduleName . '|'); + + return $this; } /** - * Resets unit and section cache + * Resets unit and section cache. * - * @return void - * @access private + * @return self */ private function resetCaches() { @@ -326,31 +309,33 @@ $this->out('Resetting ModRewrite Cache ... '); $this->_event->CallSubEvent('OnResetModRwCache'); $this->displayStatus('OK'); + + return $this; } /** - * Rebuild theme files + * Rebuild theme files. * - * @return void - * @access private + * @return self */ private function refreshThemes() { $this->out('Refreshing Theme Files ... '); $this->_event->CallSubEvent('OnRebuildThemes'); $this->displayStatus('OK'); + + return $this; } /** - * Runs database upgrade script + * Runs database upgrade script. * - * @return bool - * @access private + * @return boolean */ private function upgradeDatabase() { $this->loadAppliedRevisions(); - $this->Conn->errorHandler = Array (&$this, 'handleSqlError'); + $this->Conn->setErrorHandler(array(&$this, 'handleSqlError')); $this->out('Verifying Database Revisions ... '); @@ -360,17 +345,13 @@ $this->displayStatus('OK'); - $applied = $this->applyRevisions(); - $this->saveAppliedRevisions(); - - return $applied; + return $this->applyRevisions(); } /** * Collects database revisions from "project_upgrades.sql" file. * - * @return bool - * @access private + * @return boolean */ private function collectDatabaseRevisions() { @@ -381,15 +362,15 @@ } $sqls = file_get_contents($filename); - preg_match_all("/# r([\d]+)([^\:]*):.*?(\n|$)/s", $sqls, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); + preg_match_all("/# r([\d]+)([^\:]*):(.*?)(\n|$)/s", $sqls, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); if ( !$matches ) { $this->displayStatus('FAILED' . PHP_EOL . 'No Database Revisions Found'); return false; } - foreach ($matches as $index => $match) { + foreach ( $matches as $index => $match ) { $revision = $match[1][0]; if ( $this->revisionApplied($revision) ) { @@ -410,16 +391,16 @@ $revision_sqls = substr($sqls, $start_pos, $end_pos - $start_pos); if ( !$revision_sqls ) { - // resision without sqls + // revision without sqls continue; } - $this->revisionTitles[$revision] = trim($match[0][0]); + $this->revisionTitles[$revision] = trim($match[3][0]); $this->revisionSqls[$revision] = $revision_sqls; - $revision_lependencies = $this->parseRevisionDependencies($match[2][0]); + $revision_dependencies = $this->parseRevisionDependencies($match[2][0]); - if ( $revision_lependencies ) { - $this->revisionDependencies[$revision] = $revision_lependencies; + if ( $revision_dependencies ) { + $this->revisionDependencies[$revision] = $revision_dependencies; } } @@ -430,17 +411,16 @@ } /** - * Checks that all dependent revisions are either present now OR were applied before + * Checks that all dependent revisions are either present now OR were applied before. * - * @return bool - * @access private + * @return boolean */ private function checkRevisionDependencies() { - foreach ($this->revisionDependencies as $revision => $revision_dependencies) { - foreach ($revision_dependencies as $revision_dependency) { + foreach ( $this->revisionDependencies as $revision => $revision_dependencies ) { + foreach ( $revision_dependencies as $revision_dependency ) { if ( $this->revisionApplied($revision_dependency) ) { - // revision dependend upon already applied -> depencency fulfilled + // revision dependent upon already applied -> dependency fulfilled continue; } @@ -462,10 +442,9 @@ } /** - * Runs all pending sqls + * Runs all pending sqls. * - * @return bool - * @access private + * @return boolean */ private function applyRevisions() { @@ -474,14 +453,21 @@ } if ( $this->dryRun ) { - $this->appliedRevisions = array_merge($this->appliedRevisions, array_keys($this->revisionSqls)); + foreach ( $this->revisionSqls as $revision => $sqls ) { + $this->initLog($revision, ModuleDeploymentLog::MODE_MANUAL); + echo PHP_EOL . $this->colorText($this->revisionTitles[$revision], 'gray', true) . PHP_EOL; // 'Processing DB Revision: #' . $revision . ' ... '; + echo $this->toLog($this->colorText('SKIPPING', 'purple')); + + $this->saveLog(ModuleDeploymentLog::STATUS_SKIPPED); + } + return true; } $this->out('Upgrading Database ... ', true); - foreach ($this->revisionSqls as $revision => $sqls) { + foreach ( $this->revisionSqls as $revision => $sqls ) { echo PHP_EOL . $this->colorText($this->revisionTitles[$revision], 'gray', true) . PHP_EOL; // 'Processing DB Revision: #' . $revision . ' ... '; $sqls = str_replace("\r\n", "\n", $sqls); // convert to linux line endings @@ -490,34 +476,32 @@ $sqls = explode(";\n", $no_comment_sqls . "\n"); // ensures that last sql won't have ";" in it $sqls = array_map('trim', $sqls); - foreach ($sqls as $sql) { + $this->initLog($revision); + + foreach ( $sqls as $sql ) { if ( substr($sql, 0, 1) == '#' ) { // output comment as is - $this->toLog($sql); - echo $this->colorText($sql, 'purple') . PHP_EOL; + echo $this->toLog($this->colorText($sql, 'purple')); + continue; } elseif ( $sql ) { - $this->toLog($sql . ' ... ', false); - $escaped_sql = $this->isCommandLine ? $sql : kUtil::escape($sql); - echo mb_substr(trim(preg_replace('/(\n|\t| )+/is', ' ', $escaped_sql)), 0, self::SQL_TRIM_LENGTH) . ' ... '; - + echo $this->toLog($this->shortenQuery($sql), false); $this->Conn->Query($sql); if ( $this->Conn->hasError() ) { // consider revisions with errors applied - $this->appliedRevisions[] = $revision; + $this->saveLog(ModuleDeploymentLog::STATUS_ERROR); return false; } else { - $this->toLog('OK (' . $this->Conn->getAffectedRows() . ')'); - $this->displayStatus('OK (' . $this->Conn->getAffectedRows() . ')'); + $this->displayStatus('OK (' . $this->Conn->getAffectedRows() . ')', true, true); } } } - $this->appliedRevisions[] = $revision; + $this->saveLog(ModuleDeploymentLog::STATUS_SUCCESS); } echo PHP_EOL; @@ -526,56 +510,104 @@ } /** - * Error handler for sql errors + * Returns shortened version of SQL query. * - * @param int $code - * @param string $msg - * @param string $sql - * @return bool - * @access public + * @param string $sql SQL query. + * + * @return string */ + protected function shortenQuery($sql) + { + $escaped_sql = $this->isCommandLine ? $sql : kUtil::escape($sql); + $single_line_sql = preg_replace('/(\n|\t| )+/is', ' ', $escaped_sql); + + return mb_substr(trim($single_line_sql), 0, self::SQL_TRIM_LENGTH) . ' ... '; + } + + /** + * Initializes log record for a revision. + * + * @param integer $revision Revision. + * @param integer $mode Mode. + * + * @return self + */ + protected function initLog($revision, $mode = ModuleDeploymentLog::MODE_AUTOMATIC) + { + $this->logData = array( + 'Module' => $this->moduleName, + 'RevisionNumber' => $revision, + 'RevisionTitle' => $this->revisionTitles[$revision], + 'IPAddress' => $this->ip, + 'Output' => '', + 'Mode' => $mode, + 'Status' => ModuleDeploymentLog::STATUS_SUCCESS, + ); + + return $this; + } + + /** + * Creates log record. + * + * @param integer $status Status. + * + * @return self + */ + private function saveLog($status) + { + $this->logData['Status'] = $status; + + $log = $this->Application->recallObject('module-deployment-log', null, array('skip_autoload' => true)); + /* @var $log kDBItem */ + + $log->Clear(); + $log->SetFieldsFromHash($this->logData); + $log->Create(); + + return $this; + } + + /** + * Error handler for sql errors. + * + * @param int $code Error code. + * @param string $msg Error message. + * @param string $sql SQL query, that raised an error. + * + * @return boolean + */ public function handleSqlError($code, $msg, $sql) { - $this->toLog('FAILED' . PHP_EOL . 'SQL Error #' . $code . ': ' . $msg); + $this->displayStatus('FAILED', true, true); - $this->displayStatus('FAILED' . PHP_EOL . 'SQL Error #' . $code . ': ' . $msg); + $error_msg = 'SQL Error #' . $code . ': ' . $msg; + $this->logData['ErrorMessage'] = $error_msg; + $this->displayStatus($error_msg); + $this->out('Please execute rest of SQLs in this Revision by hand and run deployment script again.', true); return true; } /** - * Checks if given revision was already applied + * Checks if given revision was already applied. * - * @param int $revision - * @return bool - * @access private + * @param int $revision Revision. + * + * @return boolean */ private function revisionApplied($revision) { - foreach ($this->appliedRevisions as $applied_revision) { - // revision range - $applied_revision = explode('-', $applied_revision, 2); - - if ( !isset($applied_revision[1]) ) { - // convert single revision to revision range - $applied_revision[1] = $applied_revision[0]; - } - - if ( $revision >= $applied_revision[0] && $revision <= $applied_revision[1] ) { - return true; - } - } - - return false; + return isset($this->appliedRevisions[$revision]); } /** - * Returns path to given file in current module install folder + * Returns path to given file in current module install folder. * - * @param string $filename + * @param string $filename Filename. + * * @return string - * @access private */ private function getModuleFile($filename) { @@ -585,16 +617,16 @@ } /** - * Extracts revisions from string in format "(1,3,5464,23342,3243)" + * Extracts revisions from string in format "(1,3,5464,23342,3243)". * - * @param string $string - * @return Array - * @access private + * @param string $string Comma-separated revision list. + * + * @return array */ private function parseRevisionDependencies($string) { if ( !$string ) { - return Array (); + return array(); } $string = explode(',', substr($string, 1, -1)); @@ -603,18 +635,18 @@ } /** - * Applies requested color and bold attributes to given text string + * Applies requested color and bold attributes to given text string. * - * @param string $text - * @param string $color - * @param bool $bold + * @param string $text Text. + * @param string $color Color. + * @param boolean $bold Bold flag. + * * @return string - * @access private */ private function colorText($text, $color, $bold = false) { if ( $this->isCommandLine ) { - $color_map = Array ( + $color_map = array( 'black' => 30, // dark gray (in bold) 'blue' => 34, // light blue (in bold) 'green' => 32, // light green (in bold) @@ -628,15 +660,15 @@ return "\033[" . ($bold ? 1 : 0) . ";" . $color_map[$color] . "m" . $text . "\033[0m"; } - $html_color_map = Array ( - 'black' => Array ('normal' => '#000000', 'bold' => '#666666'), - 'blue' => Array ('normal' => '#00009C', 'bold' => '#3C3CFF'), - 'green' => Array ('normal' => '#009000', 'bold' => '#00FF00'), - 'cyan' => Array ('normal' => '#009C9C', 'bold' => '#00FFFF'), - 'red' => Array ('normal' => '#9C0000', 'bold' => '#FF0000'), - 'purple' => Array ('normal' => '#900090', 'bold' => '#F99CF9'), - 'brown' => Array ('normal' => '#C9C909', 'bold' => '#FFFF00'), - 'gray' => Array ('normal' => '#909090', 'bold' => '#FFFFFF'), + $html_color_map = array( + 'black' => array('normal' => '#000000', 'bold' => '#666666'), + 'blue' => array('normal' => '#00009C', 'bold' => '#3C3CFF'), + 'green' => array('normal' => '#009000', 'bold' => '#00FF00'), + 'cyan' => array('normal' => '#009C9C', 'bold' => '#00FFFF'), + 'red' => array('normal' => '#9C0000', 'bold' => '#FF0000'), + 'purple' => array('normal' => '#900090', 'bold' => '#F99CF9'), + 'brown' => array('normal' => '#C9C909', 'bold' => '#FFFF00'), + 'gray' => array('normal' => '#909090', 'bold' => '#FFFFFF'), ); $html_color = $html_color_map[$color][$bold ? 'bold' : 'normal']; @@ -645,30 +677,36 @@ } /** - * Displays last command execution status + * Displays last command execution status. * - * @param string $status_text - * @param bool $new_line - * @return void - * @access private + * @param string $status_text Status text. + * @param boolean $new_line Jump to next line. + * @param boolean $to_log Also write to log. + * + * @return self */ - private function displayStatus($status_text, $new_line = true) + private function displayStatus($status_text, $new_line = true, $to_log = false) { $color = substr($status_text, 0, 2) == 'OK' ? 'green' : 'red'; + $ret = $this->colorText($status_text, $color, false); - echo $this->colorText($status_text, $color, false); - - if ( $new_line ) { - echo PHP_EOL; + if ( $to_log ) { + echo $this->toLog($ret, $new_line); } + else { + echo $ret . ($new_line ? PHP_EOL : ''); + } + + return $this; } /** - * Outputs a text and escapes it if necessary + * Outputs a text and escapes it if necessary. * - * @param string $text - * @param bool $new_line - * @return void + * @param string $text Text. + * @param boolean $new_line Jump to next line. + * + * @return self */ private function out($text, $new_line = false) { @@ -677,5 +715,8 @@ } echo $text . ($new_line ? PHP_EOL : ''); + + return $this; } + } \ No newline at end of file