Complex classes like ReleaseController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ReleaseController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
43 | class ReleaseController extends Controller |
||
44 | { |
||
45 | public $defaultAction = 'release'; |
||
46 | |||
47 | /** |
||
48 | * @var string base path to use for releases. |
||
49 | */ |
||
50 | public $basePath; |
||
51 | /** |
||
52 | * @var bool whether to make actual changes. If true, it will run without changing or pushing anything. |
||
53 | */ |
||
54 | public $dryRun = false; |
||
55 | /** |
||
56 | * @var bool whether to fetch latest tags. |
||
57 | */ |
||
58 | public $update = false; |
||
59 | /** |
||
60 | * @var string override the default version. e.g. for major or patch releases. |
||
61 | */ |
||
62 | public $version; |
||
63 | |||
64 | |||
65 | public function options($actionID) |
||
66 | { |
||
67 | $options = ['basePath']; |
||
68 | if ($actionID === 'release') { |
||
69 | $options[] = 'dryRun'; |
||
70 | $options[] = 'version'; |
||
71 | } elseif ($actionID === 'sort-changelog') { |
||
72 | $options[] = 'version'; |
||
73 | } elseif ($actionID === 'info') { |
||
74 | $options[] = 'update'; |
||
75 | } |
||
76 | |||
77 | return array_merge(parent::options($actionID), $options); |
||
78 | } |
||
79 | |||
80 | |||
81 | public function beforeAction($action) |
||
82 | { |
||
83 | if (!$this->interactive) { |
||
84 | throw new Exception('Sorry, but releases should be run interactively to ensure you actually verify what you are doing ;)'); |
||
85 | } |
||
86 | if ($this->basePath === null) { |
||
87 | $this->basePath = \dirname(\dirname(__DIR__)); |
||
88 | } |
||
89 | $this->basePath = rtrim($this->basePath, '\\/'); |
||
90 | return parent::beforeAction($action); |
||
91 | } |
||
92 | |||
93 | /** |
||
94 | * Shows information about current framework and extension versions. |
||
95 | */ |
||
96 | public function actionInfo() |
||
97 | { |
||
98 | $items = [ |
||
99 | 'framework', |
||
100 | 'app-basic', |
||
101 | 'app-advanced', |
||
102 | ]; |
||
103 | $extensionPath = "{$this->basePath}/extensions"; |
||
104 | foreach (scandir($extensionPath) as $extension) { |
||
105 | if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) { |
||
106 | $items[] = $extension; |
||
107 | } |
||
108 | } |
||
109 | |||
110 | if ($this->update) { |
||
111 | foreach ($items as $item) { |
||
112 | $this->stdout("fetching tags for $item..."); |
||
113 | if ($item === 'framework') { |
||
114 | $this->gitFetchTags((string)$this->basePath); |
||
115 | } elseif (strncmp('app-', $item, 4) === 0) { |
||
116 | $this->gitFetchTags("{$this->basePath}/apps/" . substr($item, 4)); |
||
117 | } else { |
||
118 | $this->gitFetchTags("{$this->basePath}/extensions/$item"); |
||
119 | } |
||
120 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
121 | } |
||
122 | } else { |
||
123 | $this->stdout("\nInformation may be outdated, re-run with `--update` to fetch latest tags.\n\n"); |
||
124 | } |
||
125 | |||
126 | $versions = $this->getCurrentVersions($items); |
||
127 | $nextVersions = $this->getNextVersions($versions, self::PATCH); |
||
128 | |||
129 | // print version table |
||
130 | $w = $this->minWidth(array_keys($versions)); |
||
131 | $this->stdout(str_repeat(' ', $w + 2) . "Current Version Next Version\n", Console::BOLD); |
||
132 | foreach ($versions as $ext => $version) { |
||
133 | $this->stdout($ext . str_repeat(' ', $w + 3 - mb_strlen($ext)) . $version . ''); |
||
134 | $this->stdout(str_repeat(' ', 17 - mb_strlen($version)) . $nextVersions[$ext] . "\n"); |
||
135 | } |
||
136 | } |
||
137 | |||
138 | private function minWidth($a) |
||
139 | { |
||
140 | $w = 1; |
||
141 | foreach ($a as $s) { |
||
142 | if (($l = mb_strlen($s)) > $w) { |
||
143 | $w = $l; |
||
144 | } |
||
145 | } |
||
146 | |||
147 | return $w; |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Automation tool for making Yii framework and official extension releases. |
||
152 | * |
||
153 | * Usage: |
||
154 | * |
||
155 | * To make a release, make sure your git is clean (no uncommitted changes) and run the following command in |
||
156 | * the yii dev repo root: |
||
157 | * |
||
158 | * ``` |
||
159 | * ./build/build release framework |
||
160 | * ``` |
||
161 | * |
||
162 | * or |
||
163 | * |
||
164 | * ``` |
||
165 | * ./build/build release redis,bootstrap,apidoc |
||
166 | * ``` |
||
167 | * |
||
168 | * You may use the `--dryRun` switch to test the command without changing or pushing anything: |
||
169 | * |
||
170 | * ``` |
||
171 | * ./build/build release redis --dryRun |
||
172 | * ``` |
||
173 | * |
||
174 | * The command will guide you through the complete release process including changing of files, |
||
175 | * committing and pushing them. Each git command must be confirmed and can be skipped individually. |
||
176 | * You may adjust changes in a separate shell or your IDE while the command is waiting for confirmation. |
||
177 | * |
||
178 | * @param array $what what do you want to release? this can either be: |
||
179 | * |
||
180 | * - an extension name such as `redis` or `bootstrap`, |
||
181 | * - an application indicated by prefix `app-`, e.g. `app-basic`, |
||
182 | * - or `framework` if you want to release a new version of the framework itself. |
||
183 | * |
||
184 | * @return int |
||
185 | */ |
||
186 | public function actionRelease(array $what) |
||
187 | { |
||
188 | if (\count($what) > 1) { |
||
189 | $this->stdout("Currently only one simultaneous release is supported.\n"); |
||
190 | return 1; |
||
191 | } |
||
192 | |||
193 | $this->stdout("This is the Yii release manager\n\n", Console::BOLD); |
||
194 | |||
195 | if ($this->dryRun) { |
||
196 | $this->stdout("Running in \"dry-run\" mode, nothing will actually be changed.\n\n", Console::BOLD, Console::FG_GREEN); |
||
197 | } |
||
198 | |||
199 | $this->validateWhat($what); |
||
200 | $versions = $this->getCurrentVersions($what); |
||
201 | |||
202 | if ($this->version !== null) { |
||
203 | // if a version is explicitly given |
||
204 | $newVersions = []; |
||
205 | foreach ($versions as $k => $v) { |
||
206 | $newVersions[$k] = $this->version; |
||
207 | } |
||
208 | } else { |
||
209 | // otherwise get next patch or minor |
||
210 | $newVersions = $this->getNextVersions($versions, self::PATCH); |
||
211 | } |
||
212 | |||
213 | $this->stdout("You are about to prepare a new release for the following things:\n\n"); |
||
214 | $this->printWhat($what, $newVersions, $versions); |
||
215 | $this->stdout("\n"); |
||
216 | |||
217 | $this->stdout("Before you make a release briefly go over the changes and check if you spot obvious mistakes:\n\n", Console::BOLD); |
||
218 | $gitDir = reset($what) === 'framework' ? 'framework/' : ''; |
||
219 | $gitVersion = $versions[reset($what)]; |
||
220 | if (strncmp('app-', reset($what), 4) !== 0) { |
||
221 | $this->stdout("- no accidentally added CHANGELOG lines for other versions than this one?\n\n git diff $gitVersion.. ${gitDir}CHANGELOG.md\n\n"); |
||
222 | $this->stdout("- are all new `@since` tags for this release version?\n"); |
||
223 | } |
||
224 | $this->stdout("- other issues with code changes?\n\n git diff -w $gitVersion.. ${gitDir}\n\n"); |
||
225 | $travisUrl = reset($what) === 'framework' ? '' : '-' . reset($what); |
||
226 | $this->stdout("- are unit tests passing on travis? https://travis-ci.org/yiisoft/yii2$travisUrl/builds\n"); |
||
227 | $this->stdout("- also make sure the milestone on github is complete and no issues or PRs are left open.\n\n"); |
||
228 | $this->printWhatUrls($what, $versions); |
||
229 | $this->stdout("\n"); |
||
230 | |||
231 | if (!$this->confirm('When you continue, this tool will run cleanup jobs and update the changelog as well as other files (locally). Continue?', false)) { |
||
232 | $this->stdout("Canceled.\n"); |
||
233 | return 1; |
||
234 | } |
||
235 | |||
236 | foreach ($what as $ext) { |
||
237 | if ($ext === 'framework') { |
||
238 | $this->releaseFramework("{$this->basePath}/framework", $newVersions['framework']); |
||
239 | } elseif (strncmp('app-', $ext, 4) === 0) { |
||
240 | $this->releaseApplication(substr($ext, 4), "{$this->basePath}/apps/" . substr($ext, 4), $newVersions[$ext]); |
||
241 | } else { |
||
242 | $this->releaseExtension($ext, "{$this->basePath}/extensions/$ext", $newVersions[$ext]); |
||
243 | } |
||
244 | } |
||
245 | |||
246 | return 0; |
||
247 | } |
||
248 | |||
249 | /** |
||
250 | * This will generate application packages for download page. |
||
251 | * |
||
252 | * Usage: |
||
253 | * |
||
254 | * ``` |
||
255 | * ./build/build release/package app-basic |
||
256 | * ``` |
||
257 | * |
||
258 | * @param array $what what do you want to package? this can either be: |
||
259 | * |
||
260 | * - an application indicated by prefix `app-`, e.g. `app-basic`, |
||
261 | * |
||
262 | * @return int |
||
263 | */ |
||
264 | public function actionPackage(array $what) |
||
265 | { |
||
266 | $this->validateWhat($what, ['app']); |
||
267 | $versions = $this->getCurrentVersions($what); |
||
268 | |||
269 | $this->stdout("You are about to generate packages for the following things:\n\n"); |
||
270 | foreach ($what as $ext) { |
||
271 | if (strncmp('app-', $ext, 4) === 0) { |
||
272 | $this->stdout(' - '); |
||
273 | $this->stdout(substr($ext, 4), Console::FG_RED); |
||
274 | $this->stdout(' application version '); |
||
275 | } elseif ($ext === 'framework') { |
||
276 | $this->stdout(' - Yii Framework version '); |
||
277 | } else { |
||
278 | $this->stdout(' - '); |
||
279 | $this->stdout($ext, Console::FG_RED); |
||
280 | $this->stdout(' extension version '); |
||
281 | } |
||
282 | $this->stdout($versions[$ext], Console::BOLD); |
||
283 | $this->stdout("\n"); |
||
284 | } |
||
285 | $this->stdout("\n"); |
||
286 | |||
287 | $packagePath = "{$this->basePath}/packages"; |
||
288 | $this->stdout("Packages will be stored in $packagePath\n\n"); |
||
289 | |||
290 | if (!$this->confirm('Continue?', false)) { |
||
291 | $this->stdout("Canceled.\n"); |
||
292 | return 1; |
||
293 | } |
||
294 | |||
295 | foreach ($what as $ext) { |
||
296 | if ($ext === 'framework') { |
||
297 | throw new Exception('Can not package framework.'); |
||
298 | } elseif (strncmp('app-', $ext, 4) === 0) { |
||
299 | $this->packageApplication(substr($ext, 4), $versions[$ext], $packagePath); |
||
300 | } else { |
||
301 | throw new Exception('Can not package extension.'); |
||
302 | } |
||
303 | } |
||
304 | |||
305 | $this->stdout("\ndone. verify the versions composer installed above and push it to github!\n\n"); |
||
306 | |||
307 | return 0; |
||
308 | } |
||
309 | |||
310 | /** |
||
311 | * Sorts CHANGELOG for framework or extension. |
||
312 | * |
||
313 | * @param array $what what do you want to resort changelog for? this can either be: |
||
314 | * |
||
315 | * - an extension name such as `redis` or `bootstrap`, |
||
316 | * - or `framework` if you want to release a new version of the framework itself. |
||
317 | */ |
||
318 | public function actionSortChangelog(array $what) |
||
319 | { |
||
320 | if (\count($what) > 1) { |
||
321 | $this->stdout("Currently only one simultaneous release is supported.\n"); |
||
322 | return 1; |
||
323 | } |
||
324 | $this->validateWhat($what, ['framework', 'ext'], false); |
||
325 | |||
326 | $version = $this->version ?: array_values($this->getNextVersions($this->getCurrentVersions($what), self::PATCH))[0]; |
||
327 | $this->stdout('sorting CHANGELOG of '); |
||
328 | $this->stdout(reset($what), Console::BOLD); |
||
329 | $this->stdout(' for version '); |
||
330 | $this->stdout($version, Console::BOLD); |
||
331 | $this->stdout('...'); |
||
332 | |||
333 | $this->resortChangelogs($what, $version); |
||
334 | |||
335 | $this->stdout("done.\n", Console::BOLD, Console::FG_GREEN); |
||
336 | } |
||
337 | |||
338 | protected function printWhat(array $what, $newVersions, $versions) |
||
339 | { |
||
340 | foreach ($what as $ext) { |
||
341 | if (strncmp('app-', $ext, 4) === 0) { |
||
342 | $this->stdout(' - '); |
||
343 | $this->stdout(substr($ext, 4), Console::FG_RED); |
||
344 | $this->stdout(' application version '); |
||
345 | } elseif ($ext === 'framework') { |
||
346 | $this->stdout(' - Yii Framework version '); |
||
347 | } else { |
||
348 | $this->stdout(' - '); |
||
349 | $this->stdout($ext, Console::FG_RED); |
||
350 | $this->stdout(' extension version '); |
||
351 | } |
||
352 | $this->stdout($newVersions[$ext], Console::BOLD); |
||
353 | $this->stdout(", last release was {$versions[$ext]}\n"); |
||
354 | } |
||
355 | } |
||
356 | |||
357 | protected function printWhatUrls(array $what, $oldVersions) |
||
358 | { |
||
359 | foreach ($what as $ext) { |
||
360 | if ($ext === 'framework') { |
||
361 | $this->stdout("framework: https://github.com/yiisoft/yii2-framework/compare/{$oldVersions[$ext]}...master\n"); |
||
362 | $this->stdout("app-basic: https://github.com/yiisoft/yii2-app-basic/compare/{$oldVersions[$ext]}...master\n"); |
||
363 | $this->stdout("app-advanced: https://github.com/yiisoft/yii2-app-advanced/compare/{$oldVersions[$ext]}...master\n"); |
||
364 | } else { |
||
365 | $this->stdout($ext, Console::FG_RED); |
||
366 | $this->stdout(": https://github.com/yiisoft/yii2-$ext/compare/{$oldVersions[$ext]}...master\n"); |
||
367 | } |
||
368 | } |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * @param array $what list of items |
||
373 | * @param array $limit list of things to allow, or empty to allow any, can be `app`, `framework`, `extension` |
||
374 | * @param bool $ensureGitClean |
||
375 | * @throws \yii\base\Exception |
||
376 | */ |
||
377 | protected function validateWhat(array $what, $limit = [], $ensureGitClean = true) |
||
378 | { |
||
379 | foreach ($what as $w) { |
||
380 | if (strncmp('app-', $w, 4) === 0) { |
||
381 | if (!empty($limit) && !\in_array('app', $limit)) { |
||
382 | throw new Exception('Only the following types are allowed: ' . implode(', ', $limit) . "\n"); |
||
383 | } |
||
384 | if (!is_dir($appPath = "{$this->basePath}/apps/" . substr($w, 4))) { |
||
385 | throw new Exception("Application path does not exist: \"{$appPath}\"\n"); |
||
386 | } |
||
387 | if ($ensureGitClean) { |
||
388 | $this->ensureGitClean($appPath); |
||
389 | } |
||
390 | } elseif ($w === 'framework') { |
||
391 | if (!empty($limit) && !\in_array('framework', $limit)) { |
||
392 | throw new Exception('Only the following types are allowed: ' . implode(', ', $limit) . "\n"); |
||
393 | } |
||
394 | if (!is_dir($fwPath = "{$this->basePath}/framework")) { |
||
395 | throw new Exception("Framework path does not exist: \"{$this->basePath}/framework\"\n"); |
||
396 | } |
||
397 | if ($ensureGitClean) { |
||
398 | $this->ensureGitClean($fwPath); |
||
399 | } |
||
400 | } else { |
||
401 | if (!empty($limit) && !\in_array('ext', $limit)) { |
||
402 | throw new Exception('Only the following types are allowed: ' . implode(', ', $limit) . "\n"); |
||
403 | } |
||
404 | if (!is_dir($extPath = "{$this->basePath}/extensions/$w")) { |
||
405 | throw new Exception("Extension path for \"$w\" does not exist: \"{$this->basePath}/extensions/$w\"\n"); |
||
406 | } |
||
407 | if ($ensureGitClean) { |
||
408 | $this->ensureGitClean($extPath); |
||
409 | } |
||
410 | } |
||
411 | } |
||
412 | } |
||
413 | |||
414 | |||
415 | protected function releaseFramework($frameworkPath, $version) |
||
416 | { |
||
417 | $this->stdout("\n"); |
||
418 | $this->stdout($h = "Preparing framework release version $version", Console::BOLD); |
||
419 | $this->stdout("\n" . str_repeat('-', \strlen($h)) . "\n\n", Console::BOLD); |
||
420 | |||
421 | if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) { |
||
422 | exit(1); |
||
423 | } |
||
424 | $this->runGit('git pull', $frameworkPath); |
||
425 | |||
426 | // checks |
||
427 | |||
428 | $this->stdout('check if framework composer.json matches yii2-dev composer.json...'); |
||
429 | $this->checkComposer($frameworkPath); |
||
430 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
431 | |||
432 | // adjustments |
||
433 | |||
434 | $this->stdout('prepare classmap...', Console::BOLD); |
||
435 | $this->dryRun || Yii::$app->runAction('classmap', [$frameworkPath]); |
||
436 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
437 | |||
438 | $this->stdout('updating mimetype magic file and mime aliases...', Console::BOLD); |
||
439 | $this->dryRun || Yii::$app->runAction('mime-type', ["$frameworkPath/helpers/mimeTypes.php"], ["$frameworkPath/helpers/mimeAliases.php"]); |
||
440 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
441 | |||
442 | $this->stdout("fixing various PHPDoc style issues...\n", Console::BOLD); |
||
443 | $this->dryRun || Yii::$app->runAction('php-doc/fix', [$frameworkPath]); |
||
444 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
445 | |||
446 | $this->stdout("updating PHPDoc @property annotations...\n", Console::BOLD); |
||
447 | $this->dryRun || Yii::$app->runAction('php-doc/property', [$frameworkPath]); |
||
448 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
449 | |||
450 | $this->stdout('sorting changelogs...', Console::BOLD); |
||
451 | $this->dryRun || $this->resortChangelogs(['framework'], $version); |
||
452 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
453 | |||
454 | $this->stdout('closing changelogs...', Console::BOLD); |
||
455 | $this->dryRun || $this->closeChangelogs(['framework'], $version); |
||
456 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
457 | |||
458 | $this->stdout('updating Yii version...'); |
||
459 | $this->dryRun || $this->updateYiiVersion($frameworkPath, $version); |
||
460 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
461 | |||
462 | $this->stdout("\nIn the following you can check the above changes using git diff.\n\n"); |
||
463 | do { |
||
464 | $this->runGit('git diff --color', $frameworkPath); |
||
465 | $this->stdout("\n\n\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\n"); |
||
466 | $this->stdout("You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\n\n"); |
||
467 | } while (!$this->confirm('Type `yes` to continue, `no` to view git diff again. Continue?')); |
||
468 | |||
469 | $this->stdout("\n\n"); |
||
470 | $this->stdout(" **** RELEASE TIME! ****\n", Console::FG_YELLOW, Console::BOLD); |
||
471 | $this->stdout(" **** Commit, Tag and Push it! ****\n", Console::FG_YELLOW, Console::BOLD); |
||
472 | $this->stdout("\n\nHint: if you decide 'no' for any of the following, the command will not be executed. You may manually run them later if needed. E.g. try the release locally without pushing it.\n\n"); |
||
473 | |||
474 | $this->stdout("Make sure to have your git set up for GPG signing. The following tag and commit should be signed.\n\n"); |
||
475 | |||
476 | $this->runGit("git commit -S -a -m \"release version $version\"", $frameworkPath); |
||
477 | $this->runGit("git tag -s $version -m \"version $version\"", $frameworkPath); |
||
478 | $this->runGit('git push', $frameworkPath); |
||
479 | $this->runGit('git push --tags', $frameworkPath); |
||
480 | |||
481 | $this->stdout("\n\n"); |
||
482 | $this->stdout('CONGRATULATIONS! You have just released ', Console::FG_YELLOW, Console::BOLD); |
||
483 | $this->stdout('framework', Console::FG_RED, Console::BOLD); |
||
484 | $this->stdout(' version ', Console::FG_YELLOW, Console::BOLD); |
||
485 | $this->stdout($version, Console::BOLD); |
||
486 | $this->stdout("!\n\n", Console::FG_YELLOW, Console::BOLD); |
||
487 | |||
488 | // TODO release applications |
||
489 | // $this->composerSetStability($what, $version); |
||
490 | |||
491 | |||
492 | // $this->resortChangelogs($what, $version); |
||
493 | // $this->closeChangelogs($what, $version); |
||
494 | // $this->composerSetStability($what, $version); |
||
495 | // if (in_array('framework', $what)) { |
||
496 | // $this->updateYiiVersion($version); |
||
497 | // } |
||
498 | |||
499 | |||
500 | // if done: |
||
501 | // * ./build/build release/done framework 2.0.0-dev 2.0.0-rc |
||
502 | // * ./build/build release/done redis 2.0.0-dev 2.0.0-rc |
||
503 | // $this->openChangelogs($what, $nextVersion); |
||
504 | // $this->composerSetStability($what, 'dev'); |
||
505 | // if (in_array('framework', $what)) { |
||
506 | // $this->updateYiiVersion($devVersion); |
||
507 | // } |
||
508 | |||
509 | |||
510 | |||
511 | // prepare next release |
||
512 | |||
513 | $this->stdout("Time to prepare the next release...\n\n", Console::FG_YELLOW, Console::BOLD); |
||
514 | |||
515 | $this->stdout('opening changelogs...', Console::BOLD); |
||
516 | $nextVersion = $this->getNextVersions(['framework' => $version], self::PATCH); // TODO support other versions |
||
517 | $this->dryRun || $this->openChangelogs(['framework'], $nextVersion['framework']); |
||
518 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
519 | |||
520 | $this->stdout('updating Yii version...'); |
||
521 | $this->dryRun || $this->updateYiiVersion($frameworkPath, $nextVersion['framework'] . '-dev'); |
||
522 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
523 | |||
524 | |||
525 | $this->stdout("\n"); |
||
526 | $this->runGit('git diff --color', $frameworkPath); |
||
527 | $this->stdout("\n\n"); |
||
528 | $this->runGit('git commit -a -m "prepare for next release"', $frameworkPath); |
||
529 | $this->runGit('git push', $frameworkPath); |
||
530 | |||
531 | $this->stdout("\n\nDONE!", Console::FG_YELLOW, Console::BOLD); |
||
532 | |||
533 | $this->stdout("\n\nThe following steps are left for you to do manually:\n\n"); |
||
534 | $nextVersion2 = $this->getNextVersions($nextVersion, self::PATCH); // TODO support other versions |
||
535 | $this->stdout("- wait for your changes to be propagated to the repo and create a tag $version on https://github.com/yiisoft/yii2-framework\n\n"); |
||
536 | $this->stdout(" git clone [email protected]:yiisoft/yii2-framework.git\n"); |
||
537 | $this->stdout(" cd yii2-framework/\n"); |
||
538 | $this->stdout(" export RELEASECOMMIT=$(git log --oneline |grep $version |grep -Po \"^[0-9a-f]+\")\n"); |
||
539 | $this->stdout(" git tag -s $version -m \"version $version\" \$RELEASECOMMIT\n"); |
||
540 | $this->stdout(" git tag --verify $version\n"); |
||
541 | $this->stdout(" git push --tags\n\n"); |
||
542 | $this->stdout("- close the $version milestone on github and open new ones for {$nextVersion['framework']} and {$nextVersion2['framework']}: https://github.com/yiisoft/yii2/milestones\n"); |
||
543 | $this->stdout("- create a release on github.\n"); |
||
544 | $this->stdout("- release news and announcement.\n"); |
||
545 | $this->stdout("- update the website (will be automated soon and is only relevant for the new website).\n"); |
||
546 | $this->stdout("\n"); |
||
547 | $this->stdout("- release applications: ./build/build release app-basic\n"); |
||
548 | $this->stdout("- release applications: ./build/build release app-advanced\n"); |
||
549 | |||
550 | $this->stdout("\n"); |
||
551 | } |
||
552 | |||
553 | protected function releaseApplication($name, $path, $version) |
||
554 | { |
||
555 | $this->stdout("\n"); |
||
556 | $this->stdout($h = "Preparing release for application $name version $version", Console::BOLD); |
||
557 | $this->stdout("\n" . str_repeat('-', \strlen($h)) . "\n\n", Console::BOLD); |
||
558 | |||
559 | if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) { |
||
560 | exit(1); |
||
561 | } |
||
562 | $this->runGit('git pull', $path); |
||
563 | |||
564 | // adjustments |
||
565 | |||
566 | $this->stdout("fixing various PHPDoc style issues...\n", Console::BOLD); |
||
567 | $this->setAppAliases($name, $path); |
||
568 | $this->dryRun || Yii::$app->runAction('php-doc/fix', [$path, 'skipFrameworkRequirements' => true]); |
||
569 | $this->resetAppAliases(); |
||
570 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
571 | |||
572 | $this->stdout("updating PHPDoc @property annotations...\n", Console::BOLD); |
||
573 | $this->setAppAliases($name, $path); |
||
574 | $this->dryRun || Yii::$app->runAction('php-doc/property', [$path, 'skipFrameworkRequirements' => true]); |
||
575 | $this->resetAppAliases(); |
||
576 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
577 | |||
578 | $this->stdout("updating composer stability...\n", Console::BOLD); |
||
579 | $this->dryRun || $this->composerSetStability(["app-$name"], $version); |
||
580 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
581 | |||
582 | $this->stdout("\nIn the following you can check the above changes using git diff.\n\n"); |
||
583 | do { |
||
584 | $this->runGit('git diff --color', $path); |
||
585 | $this->stdout("\n\n\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\n"); |
||
586 | $this->stdout("You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\n\n"); |
||
587 | } while (!$this->confirm('Type `yes` to continue, `no` to view git diff again. Continue?')); |
||
588 | |||
589 | $this->stdout("\n\n"); |
||
590 | $this->stdout(" **** RELEASE TIME! ****\n", Console::FG_YELLOW, Console::BOLD); |
||
591 | $this->stdout(" **** Commit, Tag and Push it! ****\n", Console::FG_YELLOW, Console::BOLD); |
||
592 | $this->stdout("\n\nHint: if you decide 'no' for any of the following, the command will not be executed. You may manually run them later if needed. E.g. try the release locally without pushing it.\n\n"); |
||
593 | |||
594 | $this->stdout("Make sure to have your git set up for GPG signing. The following tag and commit should be signed.\n\n"); |
||
595 | |||
596 | $this->runGit("git commit -S -a -m \"release version $version\"", $path); |
||
597 | $this->runGit("git tag -s $version -m \"version $version\"", $path); |
||
598 | $this->runGit('git push', $path); |
||
599 | $this->runGit('git push --tags', $path); |
||
600 | |||
601 | $this->stdout("\n\n"); |
||
602 | $this->stdout('CONGRATULATIONS! You have just released application ', Console::FG_YELLOW, Console::BOLD); |
||
603 | $this->stdout($name, Console::FG_RED, Console::BOLD); |
||
604 | $this->stdout(' version ', Console::FG_YELLOW, Console::BOLD); |
||
605 | $this->stdout($version, Console::BOLD); |
||
606 | $this->stdout("!\n\n", Console::FG_YELLOW, Console::BOLD); |
||
607 | |||
608 | // prepare next release |
||
609 | |||
610 | $this->stdout("Time to prepare the next release...\n\n", Console::FG_YELLOW, Console::BOLD); |
||
611 | |||
612 | $this->stdout("updating composer stability...\n", Console::BOLD); |
||
613 | $this->dryRun || $this->composerSetStability(["app-$name"], 'dev'); |
||
614 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
615 | |||
616 | $nextVersion = $this->getNextVersions(["app-$name" => $version], self::PATCH); // TODO support other versions |
||
617 | |||
618 | $this->stdout("\n"); |
||
619 | $this->runGit('git diff --color', $path); |
||
620 | $this->stdout("\n\n"); |
||
621 | $this->runGit('git commit -a -m "prepare for next release"', $path); |
||
622 | $this->runGit('git push', $path); |
||
623 | |||
624 | $this->stdout("\n\nDONE!", Console::FG_YELLOW, Console::BOLD); |
||
625 | |||
626 | $this->stdout("\n\nThe following steps are left for you to do manually:\n\n"); |
||
627 | $nextVersion2 = $this->getNextVersions($nextVersion, self::PATCH); // TODO support other versions |
||
628 | $this->stdout("- close the $version milestone on github and open new ones for {$nextVersion["app-$name"]} and {$nextVersion2["app-$name"]}: https://github.com/yiisoft/yii2-app-$name/milestones\n"); |
||
629 | $this->stdout("- Create Application packages and upload them to github: ./build release/package app-$name\n"); |
||
630 | |||
631 | $this->stdout("\n"); |
||
632 | } |
||
633 | |||
634 | private $_oldAlias; |
||
635 | |||
636 | protected function setAppAliases($app, $path) |
||
637 | { |
||
638 | $this->_oldAlias = Yii::getAlias('@app'); |
||
639 | switch ($app) { |
||
640 | case 'basic': |
||
641 | Yii::setAlias('@app', $path); |
||
642 | break; |
||
643 | case 'advanced': |
||
644 | // setup @frontend, @backend etc... |
||
645 | require "$path/common/config/bootstrap.php"; |
||
646 | break; |
||
647 | } |
||
648 | } |
||
649 | |||
650 | protected function resetAppAliases() |
||
651 | { |
||
652 | Yii::setAlias('@app', $this->_oldAlias); |
||
653 | } |
||
654 | |||
655 | protected function packageApplication($name, $version, $packagePath) |
||
656 | { |
||
657 | FileHelper::createDirectory($packagePath); |
||
658 | |||
659 | $this->runCommand("composer create-project yiisoft/yii2-app-$name $name $version", $packagePath); |
||
660 | // clear cookie validation key in basic app |
||
661 | if (is_file($configFile = "$packagePath/$name/config/web.php")) { |
||
662 | $this->sed( |
||
663 | "/'cookieValidationKey' => '.*?',/", |
||
664 | "'cookieValidationKey' => '',", |
||
665 | $configFile |
||
666 | ); |
||
667 | } |
||
668 | $this->runCommand("tar zcf yii-$name-app-$version.tgz $name", $packagePath); |
||
669 | } |
||
670 | |||
671 | protected function releaseExtension($name, $path, $version) |
||
672 | { |
||
673 | $this->stdout("\n"); |
||
674 | $this->stdout($h = "Preparing release for extension $name version $version", Console::BOLD); |
||
675 | $this->stdout("\n" . str_repeat('-', \strlen($h)) . "\n\n", Console::BOLD); |
||
676 | |||
677 | if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) { |
||
678 | exit(1); |
||
679 | } |
||
680 | $this->runGit('git pull', $path); |
||
681 | |||
682 | // adjustments |
||
683 | |||
684 | $this->stdout("fixing various PHPDoc style issues...\n", Console::BOLD); |
||
685 | $this->dryRun || Yii::$app->runAction('php-doc/fix', [$path]); |
||
686 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
687 | |||
688 | $this->stdout("updating PHPDoc @property annotations...\n", Console::BOLD); |
||
689 | $this->dryRun || Yii::$app->runAction('php-doc/property', [$path]); |
||
690 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
691 | |||
692 | $this->stdout('sorting changelogs...', Console::BOLD); |
||
693 | $this->dryRun || $this->resortChangelogs([$name], $version); |
||
694 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
695 | |||
696 | $this->stdout('closing changelogs...', Console::BOLD); |
||
697 | $this->dryRun || $this->closeChangelogs([$name], $version); |
||
698 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
699 | |||
700 | $this->stdout("\nIn the following you can check the above changes using git diff.\n\n"); |
||
701 | do { |
||
702 | $this->runGit('git diff --color', $path); |
||
703 | $this->stdout("\n\n\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\n"); |
||
704 | $this->stdout("You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\n\n"); |
||
705 | } while (!$this->confirm('Type `yes` to continue, `no` to view git diff again. Continue?')); |
||
706 | |||
707 | $this->stdout("\n\n"); |
||
708 | $this->stdout(" **** RELEASE TIME! ****\n", Console::FG_YELLOW, Console::BOLD); |
||
709 | $this->stdout(" **** Commit, Tag and Push it! ****\n", Console::FG_YELLOW, Console::BOLD); |
||
710 | $this->stdout("\n\nHint: if you decide 'no' for any of the following, the command will not be executed. You may manually run them later if needed. E.g. try the release locally without pushing it.\n\n"); |
||
711 | |||
712 | $this->stdout("Make sure to have your git set up for GPG signing. The following tag and commit should be signed.\n\n"); |
||
713 | |||
714 | $this->runGit("git commit -S -a -m \"release version $version\"", $path); |
||
715 | $this->runGit("git tag -s $version -m \"version $version\"", $path); |
||
716 | $this->runGit('git push', $path); |
||
717 | $this->runGit('git push --tags', $path); |
||
718 | |||
719 | $this->stdout("\n\n"); |
||
720 | $this->stdout('CONGRATULATIONS! You have just released extension ', Console::FG_YELLOW, Console::BOLD); |
||
721 | $this->stdout($name, Console::FG_RED, Console::BOLD); |
||
722 | $this->stdout(' version ', Console::FG_YELLOW, Console::BOLD); |
||
723 | $this->stdout($version, Console::BOLD); |
||
724 | $this->stdout("!\n\n", Console::FG_YELLOW, Console::BOLD); |
||
725 | |||
726 | // prepare next release |
||
727 | |||
728 | $this->stdout("Time to prepare the next release...\n\n", Console::FG_YELLOW, Console::BOLD); |
||
729 | |||
730 | $this->stdout('opening changelogs...', Console::BOLD); |
||
731 | $nextVersion = $this->getNextVersions([$name => $version], self::PATCH); // TODO support other versions |
||
732 | $this->dryRun || $this->openChangelogs([$name], $nextVersion[$name]); |
||
733 | $this->stdout("done.\n", Console::FG_GREEN, Console::BOLD); |
||
734 | |||
735 | $this->stdout("\n"); |
||
736 | $this->runGit('git diff --color', $path); |
||
737 | $this->stdout("\n\n"); |
||
738 | $this->runGit('git commit -a -m "prepare for next release"', $path); |
||
739 | $this->runGit('git push', $path); |
||
740 | |||
741 | $this->stdout("\n\nDONE!", Console::FG_YELLOW, Console::BOLD); |
||
742 | |||
743 | $this->stdout("\n\nThe following steps are left for you to do manually:\n\n"); |
||
744 | $nextVersion2 = $this->getNextVersions($nextVersion, self::PATCH); // TODO support other versions |
||
745 | $this->stdout("- close the $version milestone on github and open new ones for {$nextVersion[$name]} and {$nextVersion2[$name]}: https://github.com/yiisoft/yii2-$name/milestones\n"); |
||
746 | $this->stdout("- release news and announcement.\n"); |
||
747 | $this->stdout("- update the website (will be automated soon and is only relevant for the new website).\n"); |
||
748 | |||
749 | $this->stdout("\n"); |
||
750 | } |
||
751 | |||
752 | |||
753 | protected function runCommand($cmd, $path) |
||
754 | { |
||
755 | $this->stdout("running $cmd ...", Console::BOLD); |
||
756 | if ($this->dryRun) { |
||
757 | $this->stdout("dry run, command `$cmd` not executed.\n"); |
||
758 | return; |
||
759 | } |
||
760 | chdir($path); |
||
761 | exec($cmd, $output, $ret); |
||
762 | if ($ret != 0) { |
||
763 | echo implode("\n", $output); |
||
764 | throw new Exception("Command \"$cmd\" failed with code " . $ret); |
||
765 | } |
||
766 | $this->stdout("\ndone.\n", Console::BOLD, Console::FG_GREEN); |
||
767 | } |
||
768 | |||
769 | protected function runGit($cmd, $path) |
||
770 | { |
||
771 | if ($this->confirm("Run `$cmd`?", true)) { |
||
772 | if ($this->dryRun) { |
||
773 | $this->stdout("dry run, command `$cmd` not executed.\n"); |
||
774 | return; |
||
775 | } |
||
776 | chdir($path); |
||
777 | exec($cmd, $output, $ret); |
||
778 | echo implode("\n", $output); |
||
779 | if ($ret != 0) { |
||
780 | throw new Exception("Command \"$cmd\" failed with code " . $ret); |
||
781 | } |
||
782 | echo "\n"; |
||
783 | } |
||
784 | } |
||
785 | |||
786 | protected function ensureGitClean($path) |
||
787 | { |
||
788 | chdir($path); |
||
789 | exec('git status --porcelain -uno', $changes, $ret); |
||
790 | if ($ret != 0) { |
||
791 | throw new Exception('Command "git status --porcelain -uno" failed with code ' . $ret); |
||
792 | } |
||
793 | if (!empty($changes)) { |
||
794 | throw new Exception("You have uncommitted changes in $path: " . print_r($changes, true)); |
||
795 | } |
||
796 | } |
||
797 | |||
798 | protected function gitFetchTags($path) |
||
799 | { |
||
800 | try { |
||
801 | chdir($path); |
||
802 | } catch (\yii\base\ErrorException $e) { |
||
803 | throw new Exception('Failed to getch git tags in ' . $path . ': ' . $e->getMessage()); |
||
804 | } |
||
805 | exec('git fetch --tags', $output, $ret); |
||
806 | if ($ret != 0) { |
||
807 | throw new Exception('Command "git fetch --tags" failed with code ' . $ret); |
||
808 | } |
||
809 | } |
||
810 | |||
811 | |||
812 | protected function checkComposer($fwPath) |
||
813 | { |
||
814 | if (!$this->confirm("\nNot yet automated: Please check if composer.json dependencies in framework dir match the one in repo root. Continue?", false)) { |
||
815 | exit; |
||
816 | } |
||
817 | } |
||
818 | |||
819 | |||
820 | protected function closeChangelogs($what, $version) |
||
821 | { |
||
822 | $v = str_replace('\\-', '[\\- ]', preg_quote($version, '/')); |
||
823 | $headline = $version . ' ' . date('F d, Y'); |
||
824 | $this->sed( |
||
825 | '/' . $v . ' under development\R(-+?)\R/', |
||
826 | $headline . "\n" . str_repeat('-', \strlen($headline)) . "\n", |
||
827 | $this->getChangelogs($what) |
||
828 | ); |
||
829 | } |
||
830 | |||
831 | protected function openChangelogs($what, $version) |
||
832 | { |
||
833 | $headline = "\n$version under development\n"; |
||
834 | $headline .= str_repeat('-', \strlen($headline) - 2) . "\n\n- no changes in this release.\n"; |
||
835 | foreach ($this->getChangelogs($what) as $file) { |
||
836 | $lines = explode("\n", file_get_contents($file)); |
||
837 | $hl = [ |
||
838 | array_shift($lines), |
||
839 | array_shift($lines), |
||
840 | ]; |
||
841 | array_unshift($lines, $headline); |
||
842 | |||
843 | file_put_contents($file, implode("\n", array_merge($hl, $lines))); |
||
844 | } |
||
845 | } |
||
846 | |||
847 | protected function resortChangelogs($what, $version) |
||
848 | { |
||
849 | foreach ($this->getChangelogs($what) as $file) { |
||
850 | // split the file into relevant parts |
||
851 | list($start, $changelog, $end) = $this->splitChangelog($file, $version); |
||
852 | $changelog = $this->resortChangelog($changelog); |
||
853 | file_put_contents($file, implode("\n", array_merge($start, $changelog, $end))); |
||
854 | } |
||
855 | } |
||
856 | |||
857 | /** |
||
858 | * Extract changelog content for a specific version. |
||
859 | * @param string $file |
||
860 | * @param string $version |
||
861 | * @return array |
||
862 | */ |
||
863 | protected function splitChangelog($file, $version) |
||
864 | { |
||
865 | $lines = explode("\n", file_get_contents($file)); |
||
866 | |||
867 | // split the file into relevant parts |
||
868 | $start = []; |
||
869 | $changelog = []; |
||
870 | $end = []; |
||
871 | |||
872 | $state = 'start'; |
||
873 | foreach ($lines as $l => $line) { |
||
874 | // starting from the changelogs headline |
||
875 | if (isset($lines[$l - 2]) && strpos($lines[$l - 2], $version) !== false && |
||
876 | isset($lines[$l - 1]) && strncmp($lines[$l - 1], '---', 3) === 0) { |
||
877 | $state = 'changelog'; |
||
878 | } |
||
879 | if ($state === 'changelog' && isset($lines[$l + 1]) && strncmp($lines[$l + 1], '---', 3) === 0) { |
||
880 | $state = 'end'; |
||
881 | } |
||
882 | // add continued lines to the last item to keep them together |
||
883 | if (!empty(${$state}) && trim($line) !== '' && strncmp($line, '- ', 2) !== 0) { |
||
884 | end(${$state}); |
||
885 | ${$state}[key(${$state})] .= "\n" . $line; |
||
886 | } else { |
||
887 | ${$state}[] = $line; |
||
888 | } |
||
889 | } |
||
890 | |||
891 | return [$start, $changelog, $end]; |
||
892 | } |
||
893 | |||
894 | /** |
||
895 | * Ensure sorting of the changelog lines. |
||
896 | * @param string[] $changelog |
||
897 | * @return string[] |
||
898 | */ |
||
899 | protected function resortChangelog($changelog) |
||
900 | { |
||
901 | // cleanup whitespace |
||
902 | foreach ($changelog as $i => $line) { |
||
903 | $changelog[$i] = rtrim($line); |
||
904 | } |
||
905 | $changelog = array_filter($changelog); |
||
906 | |||
907 | $i = 0; |
||
908 | ArrayHelper::multisort($changelog, function ($line) use (&$i) { |
||
909 | if (preg_match('/^- (Chg|Enh|Bug|New)( #\d+(, #\d+)*)?: .+/', $line, $m)) { |
||
910 | $o = ['Bug' => 'C', 'Enh' => 'D', 'Chg' => 'E', 'New' => 'F']; |
||
911 | return $o[$m[1]] . ' ' . (!empty($m[2]) ? $m[2] : 'AAAA' . $i++); |
||
912 | } |
||
913 | |||
914 | return 'B' . $i++; |
||
915 | }, SORT_ASC, SORT_NATURAL); |
||
916 | |||
917 | // re-add leading and trailing lines |
||
918 | array_unshift($changelog, ''); |
||
919 | $changelog[] = ''; |
||
920 | $changelog[] = ''; |
||
921 | |||
922 | return $changelog; |
||
923 | } |
||
924 | |||
925 | protected function getChangelogs($what) |
||
926 | { |
||
927 | $changelogs = []; |
||
928 | if (\in_array('framework', $what)) { |
||
929 | $changelogs[] = $this->getFrameworkChangelog(); |
||
930 | } |
||
931 | |||
932 | return array_merge($changelogs, $this->getExtensionChangelogs($what)); |
||
933 | } |
||
934 | |||
935 | protected function getFrameworkChangelog() |
||
936 | { |
||
937 | return $this->basePath . '/framework/CHANGELOG.md'; |
||
938 | } |
||
939 | |||
940 | protected function getExtensionChangelogs($what) |
||
941 | { |
||
942 | return array_filter(glob($this->basePath . '/extensions/*/CHANGELOG.md'), function ($elem) use ($what) { |
||
943 | foreach ($what as $ext) { |
||
944 | if (strpos($elem, "extensions/$ext/CHANGELOG.md") !== false) { |
||
945 | return true; |
||
946 | } |
||
947 | } |
||
948 | |||
949 | return false; |
||
950 | }); |
||
951 | } |
||
952 | |||
953 | protected function composerSetStability($what, $version) |
||
954 | { |
||
955 | $apps = []; |
||
956 | if (\in_array('app-advanced', $what)) { |
||
957 | $apps[] = $this->basePath . '/apps/advanced/composer.json'; |
||
958 | } |
||
959 | if (\in_array('app-basic', $what)) { |
||
960 | $apps[] = $this->basePath . '/apps/basic/composer.json'; |
||
961 | } |
||
962 | if (\in_array('app-benchmark', $what)) { |
||
963 | $apps[] = $this->basePath . '/apps/benchmark/composer.json'; |
||
964 | } |
||
965 | if (empty($apps)) { |
||
966 | return; |
||
967 | } |
||
968 | |||
969 | $stability = 'stable'; |
||
970 | if (strpos($version, 'alpha') !== false) { |
||
971 | $stability = 'alpha'; |
||
972 | } elseif (strpos($version, 'beta') !== false) { |
||
973 | $stability = 'beta'; |
||
974 | } elseif (strpos($version, 'rc') !== false) { |
||
975 | $stability = 'RC'; |
||
976 | } elseif (strpos($version, 'dev') !== false) { |
||
977 | $stability = 'dev'; |
||
978 | } |
||
979 | |||
980 | $this->sed( |
||
981 | '/"minimum-stability": "(.+?)",/', |
||
982 | '"minimum-stability": "' . $stability . '",', |
||
983 | $apps |
||
984 | ); |
||
985 | } |
||
986 | |||
987 | protected function updateYiiVersion($frameworkPath, $version) |
||
988 | { |
||
989 | $this->sed( |
||
990 | '/function getVersion\(\)\R \{\R return \'(.+?)\';/', |
||
991 | "function getVersion()\n {\n return '$version';", |
||
992 | $frameworkPath . '/BaseYii.php'); |
||
993 | } |
||
994 | |||
995 | protected function sed($pattern, $replace, $files) |
||
996 | { |
||
997 | foreach ((array) $files as $file) { |
||
998 | file_put_contents($file, preg_replace($pattern, $replace, file_get_contents($file))); |
||
999 | } |
||
1000 | } |
||
1001 | |||
1002 | protected function getCurrentVersions(array $what) |
||
1003 | { |
||
1004 | $versions = []; |
||
1005 | foreach ($what as $ext) { |
||
1006 | if ($ext === 'framework') { |
||
1007 | chdir("{$this->basePath}/framework"); |
||
1008 | } elseif (strncmp('app-', $ext, 4) === 0) { |
||
1009 | chdir("{$this->basePath}/apps/" . substr($ext, 4)); |
||
1010 | } else { |
||
1011 | chdir("{$this->basePath}/extensions/$ext"); |
||
1012 | } |
||
1013 | $tags = []; |
||
1014 | exec('git tag', $tags, $ret); |
||
1015 | if ($ret != 0) { |
||
1016 | throw new Exception('Command "git tag" failed with code ' . $ret); |
||
1017 | } |
||
1018 | rsort($tags, SORT_NATURAL); // TODO this can not deal with alpha/beta/rc... |
||
1019 | $versions[$ext] = reset($tags); |
||
1020 | } |
||
1021 | |||
1022 | return $versions; |
||
1023 | } |
||
1024 | |||
1025 | const MINOR = 'minor'; |
||
1026 | const PATCH = 'patch'; |
||
1027 | |||
1028 | protected function getNextVersions(array $versions, $type) |
||
1029 | { |
||
1030 | foreach ($versions as $k => $v) { |
||
1031 | if (empty($v)) { |
||
1032 | $versions[$k] = '2.0.0'; |
||
1033 | continue; |
||
1034 | } |
||
1035 | $parts = explode('.', $v); |
||
1036 | switch ($type) { |
||
1037 | case self::MINOR: |
||
1038 | $parts[1]++; |
||
1039 | $parts[2] = 0; |
||
1040 | if (isset($parts[3])) { |
||
1041 | unset($parts[3]); |
||
1042 | } |
||
1043 | break; |
||
1044 | case self::PATCH: |
||
1045 | $parts[2]++; |
||
1046 | if (isset($parts[3])) { |
||
1047 | unset($parts[3]); |
||
1048 | } |
||
1049 | break; |
||
1050 | default: |
||
1051 | throw new Exception('Unknown version type.'); |
||
1052 | } |
||
1053 | $versions[$k] = implode('.', $parts); |
||
1054 | } |
||
1055 | |||
1056 | return $versions; |
||
1057 | } |
||
1058 | } |
||
1059 |