From a7c3a4a0e25ebf00fe13de1f0db4c1dcc639c2cb Mon Sep 17 00:00:00 2001 From: Joe Green Date: Wed, 12 Feb 2014 16:23:58 +0000 Subject: [PATCH 1/3] Some code --- bin/up.php | 12 ++++ src/Smrtr/MysqlVersionControl/Controller.php | 33 ++++++++++ src/Smrtr/MysqlVersionControl/DbConfig.php | 58 ++++++++++++++++++ src/Smrtr/MysqlVersionControl/UpCommand.php | 63 ++++++++++++++++++++ src/Smrtr/MysqlVersionControlException.php | 13 ++++ 5 files changed, 179 insertions(+) create mode 100644 bin/up.php create mode 100644 src/Smrtr/MysqlVersionControl/Controller.php create mode 100644 src/Smrtr/MysqlVersionControl/DbConfig.php create mode 100644 src/Smrtr/MysqlVersionControl/UpCommand.php create mode 100644 src/Smrtr/MysqlVersionControlException.php diff --git a/bin/up.php b/bin/up.php new file mode 100644 index 0000000..f900e8c --- /dev/null +++ b/bin/up.php @@ -0,0 +1,12 @@ +add( + new \Smrtr\MysqlVersionControl\UpCommand($env) + ); +} + +$app->run(); diff --git a/src/Smrtr/MysqlVersionControl/Controller.php b/src/Smrtr/MysqlVersionControl/Controller.php new file mode 100644 index 0000000..917e5e4 --- /dev/null +++ b/src/Smrtr/MysqlVersionControl/Controller.php @@ -0,0 +1,33 @@ +toArray(); + return $config['environments']; + } + + public static function getPDO($env, $buildtime = false) + { + $key = $buildtime ? 'buildtime' : 'runtime'; + $config = self::getConfig($env); + $config = $config[$key]; + + $dsn = sprintf('mysql:host=%s;dbname=%s', $config['host'], $config['dbname']); + $db = new \PDO($dsn, $config['user'], $config['password']); + + return $db; + } + + public static function getConfig($env) + { + $config = new \Zend_Config_Ini(self::getConfigFile(), $env); + return $config->toArray(); + } + + protected static function getConfigFile() + { + $configFile = self::getProjectPath() . '/db/db.ini'; + + if (!is_readable($configFile)) { + throw new MysqlVersionControlException( + "Cannot find or open database config; looked in '$configFile'" + ); + } + + return $configFile; + } + + protected static function getProjectPath() + { + $parts = explode('/vendor', __FILE__); + array_pop($parts); + return implode('/vendor', $parts); + } +} diff --git a/src/Smrtr/MysqlVersionControl/UpCommand.php b/src/Smrtr/MysqlVersionControl/UpCommand.php new file mode 100644 index 0000000..31fe039 --- /dev/null +++ b/src/Smrtr/MysqlVersionControl/UpCommand.php @@ -0,0 +1,63 @@ +env = $env; + parent::__construct(); + } + + protected function configure() + { + $this + ->setName($this->env) + ->setDescription('Install the '.$this->env.' versions') + ->addArgument( + 'mysqlbin', + InputArgument::OPTIONAL, + 'Where is the MySQL binary located?' + ) + ->addOption( + 'confirm', + null, + InputOption::VALUE_NONE, + 'If set, the command will bypass the confirmation prompt' + ) + ; + } + + /** + * Load a few settings then run the installer. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int|null|void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + Controller::update( + $this->env, + $output, + $this->getHelperSet()->get('dialog'), + (bool) $input->getOption('confirm'), + $input->getArgument('mysqlbin') ?: 'mysql' + ); + } +} diff --git a/src/Smrtr/MysqlVersionControlException.php b/src/Smrtr/MysqlVersionControlException.php new file mode 100644 index 0000000..4d1a7b8 --- /dev/null +++ b/src/Smrtr/MysqlVersionControlException.php @@ -0,0 +1,13 @@ + Date: Wed, 12 Feb 2014 16:46:10 +0000 Subject: [PATCH 2/3] Removed controller --- src/Smrtr/MysqlVersionControl/Controller.php | 33 -------------------- 1 file changed, 33 deletions(-) delete mode 100644 src/Smrtr/MysqlVersionControl/Controller.php diff --git a/src/Smrtr/MysqlVersionControl/Controller.php b/src/Smrtr/MysqlVersionControl/Controller.php deleted file mode 100644 index 917e5e4..0000000 --- a/src/Smrtr/MysqlVersionControl/Controller.php +++ /dev/null @@ -1,33 +0,0 @@ - Date: Wed, 12 Feb 2014 17:03:32 +0000 Subject: [PATCH 3/3] More code for up command --- src/Smrtr/MysqlVersionControl/DbConfig.php | 7 + src/Smrtr/MysqlVersionControl/UpCommand.php | 196 +++++++++++++++++++- 2 files changed, 196 insertions(+), 7 deletions(-) diff --git a/src/Smrtr/MysqlVersionControl/DbConfig.php b/src/Smrtr/MysqlVersionControl/DbConfig.php index 17c42f1..4afa128 100644 --- a/src/Smrtr/MysqlVersionControl/DbConfig.php +++ b/src/Smrtr/MysqlVersionControl/DbConfig.php @@ -18,6 +18,13 @@ public static function getEnvironments() return $config['environments']; } + public static function getTestingEnvironments() + { + $config = new \Zend_Config_Ini(self::getConfigFile(), 'environments'); + $config = $config->toArray(); + return $config['testing_environments']; + } + public static function getPDO($env, $buildtime = false) { $key = $buildtime ? 'buildtime' : 'runtime'; diff --git a/src/Smrtr/MysqlVersionControl/UpCommand.php b/src/Smrtr/MysqlVersionControl/UpCommand.php index 31fe039..6f9a8a1 100644 --- a/src/Smrtr/MysqlVersionControl/UpCommand.php +++ b/src/Smrtr/MysqlVersionControl/UpCommand.php @@ -2,12 +2,12 @@ namespace Smrtr\MysqlVersionControl; -use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; /** * Class UpCommand @@ -52,12 +52,194 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - Controller::update( - $this->env, - $output, - $this->getHelperSet()->get('dialog'), - (bool) $input->getOption('confirm'), - $input->getArgument('mysqlbin') ?: 'mysql' + $mysqlbin = $input->getArgument('mysqlbin') ?: 'mysql'; + $buildConf = DbConfig::getPDO($this->env, true); + $runConf = DbConfig::getPDO($this->env); + + // 1. Make sure that db_config table is present + + $output->writeln(''); + $output->writeln('Checking database status... '); + $output->writeln(''); + + + if (!$buildConf instanceof \PDO) { + $output->writeln('Failed: unable to obtain a database connection.'); + return false; + } + + if ($buildConf->query("SHOW TABLES LIKE 'db_config'")->rowCount()) { + + $output->writeln('Database version control is already installed.'); + self::$checkList['db-schema'] = true; + return true; + + } else { + + $output->writeln('Installing version control...'); + + $result = $buildConf->query( +"CREATE TABLE `db_config` +( + `key` VARCHAR(50) COLLATE 'utf8_general_ci' NOT NULL, + `value` TEXT, + `created_at` DATETIME, + `updated_at` DATETIME, + PRIMARY KEY (`key`), + UNIQUE INDEX `db_config_U_1` (`key`) +) ENGINE=MyISAM;" + ) + ->execute(); + + if (!$result) { + $output->writeln('Installing version control failed.'); + return false; + } + + $output->writeln('Installed version control successfully.'); + } + + // 2. Check for current version and available version + + // what is the current version? + $query = $runConf->query("SELECT `value` FROM `db_config` WHERE `key`='version'"); + if ($query->rowCount()) { + $versionRow = $query->fetch(\PDO::FETCH_ASSOC); + $currentVersion = (int) $versionRow['value']; + } else { + $currentVersion = 0; + } + + // what is the available version? + $availableVersion = 0; + $versionsPath = realpath(dirname(__FILE__).'/../../../../../../db/versions'); + foreach (scandir($versionsPath) as $path) { + if (preg_match("/^(\\d)+$/", $path) && (int) $path > $availableVersion) { + $availableVersion = (int) $path; + } + } + + if ($currentVersion >= $availableVersion) { + $output->writeln('Database version is already up to date.'); + return true; + } + + $noun = ($availableVersion - $currentVersion > 1) ? 'updates' : 'update'; + $output->writeln( + "Installing database $noun (Current version: $currentVersion, Available version: $availableVersion)..." ); + + // go from current to latest version, building stack of SQL files + $filesToLookFor = []; + $filesToLookFor[] = 'schema.sql'; // structural changes, alters, creates, drops + $filesToLookFor[] = 'data.sql'; // core data, inserts, replaces, updates, deletes + if (in_array($this->env, DbConfig::getTestingEnvironments())) { + $filesToLookFor[] = 'testing.sql'; // extra data on top of data.sql for the testing environment(s) + } + $filesToLookFor[] = 'runme.php'; // custom php hook + + $stack = array(); + for ($i = $currentVersion + 1; $i <= $availableVersion; $i++) { + + $path = $versionsPath.DIRECTORY_SEPARATOR.$i; + if (!is_dir($path) || !is_readable($path)) { + continue; + } + + foreach ($filesToLookFor as $file) { + if (is_readable($path.DIRECTORY_SEPARATOR.$file)) { + $stack[$i][$file] = $path.DIRECTORY_SEPARATOR.$file; + } + } + } + + $s = '\\' == DIRECTORY_SEPARATOR ? "%s" : "'%s'"; // Windows doesn't like quoted params + $cmdMySQL = "$mysqlbin -h $s --user=$s --password=$s --database=$s < %s"; + + // loop sql file stack and execute on mysql CLI + + $dbConf = DbConfig::getConfig($this->env); + + $previousVersion = $currentVersion; + $result = true; + foreach ($stack as $version => $files) { + + $output->write($previousVersion." -> $version "); + + if (!$result) { + $output->write('skipped'); + continue; + } + + foreach ($files as $file) { + + if ('schema.sql' === $file) { + $conf = $dbConf['buildtime']; + } else { + $conf = $dbConf['runtime']; + } + $host = $conf['host']; + $user = $conf['user']; + $pass = $conf['password']; + $name = $conf['database']; + + if ('.sql' === substr($file, -4)) { + + $command = sprintf( + $cmdMySQL, + $host, + $user, + $pass, + $name, + $file + ); + + $process = new Process($command); + $process->run(); + + if (!$process->isSuccessful()) { + $result = false; + break; + } + + continue; + } + + if ('.php' === substr($file, -4)) { + + $feedback = require_once $file; + } + } + + if ($result) { + $result = $buildConf->query( + "REPLACE INTO `db_config` (`key`, `value`, `updated_at`) VALUES ('version', $version, now())" + )->execute(); + } + + $statusMsg = $result ? 'OK' : 'Failed'; + $output->write($statusMsg, true); + + if (isset($feedback) && is_string($feedback) && strlen($feedback)) { + $output->write($feedback); + unset($feedback); + } + + if (!$result) { + $output->write(''.$process->getErrorOutput().''); + } + + $previousVersion = $version; + } + + if ($result) { + $output->writeln('Database updates installed successfully.'); + self::$checkList['db-update'] = true; + return true; + + } else { + $output->writeln('Installing database updates failed.'); + return false; + } } }