* @since 2.0 */ class ReleaseController extends Controller { public $defaultAction = 'help'; /** * Usage: * * ``` * ./build/build release/prepare framework 2.0.0-beta * ./build/build release/prepare redis 2.0.0-beta * ``` * */ public function actionPrepare(array $what, $version) { $this->resortChangelogs($what, $version); $this->closeChangelogs($what, $version); $this->composerSetStability($what, $version); if (in_array('framework', $what)) { $this->updateYiiVersion($version); } } /** * Usage: * * ``` * ./build/build release/done framework 2.0.0-dev 2.0.0-rc * ./build/build release/done redis 2.0.0-dev 2.0.0-rc * ``` */ public function actionDone(array $what, $devVersion, $nextVersion) { $this->openChangelogs($what, $nextVersion); $this->composerSetStability($what, 'dev'); if (in_array('framework', $what)) { $this->updateYiiVersion($devVersion); } } protected function closeChangelogs($what, $version) { $v = str_replace('\\-', '[\\- ]', preg_quote($version, '/')); $headline = $version . ' ' . date('F d, Y'); $this->sed( '/'.$v.' under development\n(-+?)\n/', $headline . "\n" . str_repeat('-', strlen($headline)) . "\n", $this->getChangelogs($what) ); } protected function openChangelogs($what, $version) { $headline = "\n$version under development\n"; $headline .= str_repeat('-', strlen($headline) - 2) . "\n\n"; foreach($this->getChangelogs($what) as $file) { $lines = explode("\n", file_get_contents($file)); $hl = [ array_shift($lines), array_shift($lines), ]; array_unshift($lines, $headline); file_put_contents($file, implode("\n", array_merge($hl, $lines))); } } protected function resortChangelogs($what, $version) { foreach($this->getChangelogs($what) as $file) { // split the file into relevant parts list($start, $changelog, $end) = $this->splitChangelog($file, $version); $changelog = $this->resortChangelog($changelog); file_put_contents($file, implode("\n", array_merge($start, $changelog, $end))); } } /** * Extract changelog content for a specific version */ protected function splitChangelog($file, $version) { $lines = explode("\n", file_get_contents($file)); // split the file into relevant parts $start = []; $changelog = []; $end = []; $state = 'start'; foreach($lines as $l => $line) { // starting from the changelogs headline if (isset($lines[$l-2]) && strpos($lines[$l-2], $version) !== false && isset($lines[$l-1]) && strncmp($lines[$l-1], '---', 3) === 0) { $state = 'changelog'; } if ($state === 'changelog' && isset($lines[$l+1]) && strncmp($lines[$l+1], '---', 3) === 0) { $state = 'end'; } ${$state}[] = $line; } return [$start, $changelog, $end]; } /** * Ensure sorting of the changelog lines */ protected function resortChangelog($changelog) { // cleanup whitespace foreach($changelog as $i => $line) { $changelog[$i] = rtrim($line); } $changelog = array_filter($changelog); $i = 0; ArrayHelper::multisort($changelog, function($line) use (&$i) { if (preg_match('/^- (Chg|Enh|Bug|New)( #\d+(, #\d+)*)?: .+$/', $line, $m)) { $o = ['Bug' => 'C', 'Enh' => 'D', 'Chg' => 'E', 'New' => 'F']; return $o[$m[1]] . ' ' . (!empty($m[2]) ? $m[2] : 'AAAA' . $i++); } return 'B' . $i++; }, SORT_ASC, SORT_NATURAL); // re-add leading and trailing lines array_unshift($changelog, ''); $changelog[] = ''; $changelog[] = ''; return $changelog; } protected function getChangelogs($what) { $changelogs = []; if (in_array('framework', $what)) { $changelogs[] = $this->getFrameworkChangelog(); } return array_merge($changelogs, $this->getExtensionChangelogs($what)); } protected function getFrameworkChangelog() { return YII2_PATH . '/CHANGELOG.md'; } protected function getExtensionChangelogs($what) { return array_filter(glob(dirname(YII2_PATH) . '/extensions/*/CHANGELOG.md'), function($elem) use ($what) { foreach($what as $ext) { if (strpos($elem, "extensions/$ext/CHANGELOG.md") !== false) { return true; } } return false; }); } protected function composerSetStability($what, $version) { $apps = []; if (in_array('app-advanced', $what)) { $apps[] = dirname(YII2_PATH) . '/apps/advanced/composer.json'; } if (in_array('app-basic', $what)) { $apps[] = dirname(YII2_PATH) . '/apps/basic/composer.json'; } if (in_array('app-benchmark', $what)) { $apps[] = dirname(YII2_PATH) . '/apps/benchmark/composer.json'; } if (empty($apps)) { return; } $stability = 'stable'; if (strpos($version, 'alpha') !== false) { $stability = 'alpha'; } elseif (strpos($version, 'beta') !== false) { $stability = 'beta'; } elseif (strpos($version, 'rc') !== false) { $stability = 'RC'; } elseif (strpos($version, 'dev') !== false) { $stability = 'dev'; } $this->sed( '/"minimum-stability": "(.+?)",/', '"minimum-stability": "' . $stability . '",', $apps ); } protected function updateYiiVersion($version) { $this->sed( '/function getVersion\(\)\n \{\n return \'(.+?)\';/', "function getVersion()\n {\n return '$version';", YII2_PATH . '/BaseYii.php'); } protected function sed($pattern, $replace, $files) { foreach((array) $files as $file) { file_put_contents($file, preg_replace($pattern, $replace, file_get_contents($file))); } } }