diff --git a/src/Changelog.php b/src/Changelog.php index 1efb055..4624bd0 100644 --- a/src/Changelog.php +++ b/src/Changelog.php @@ -54,6 +54,8 @@ public function generate(InputInterface $input, SymfonyStyle $output): int $fromTag = $input->getOption('from-tag'); $toTag = $input->getOption('to-tag'); $history = $input->getOption('history'); + $sortBy = $this->config->getSortBy(); + $sortOrientation = $this->config->getSortOrientation($sortBy); $firstRelease = $input->getOption('first-release'); $alphaRelease = $input->getOption('alpha'); @@ -73,6 +75,9 @@ public function generate(InputInterface $input, SymfonyStyle $output): int // Set working directory chdir($root); + // Hook pre run + $this->config->preRun(); + if (!Repository::isInsideWorkTree()) { $output->error('Not a git repository'); @@ -251,6 +256,17 @@ public function generate(InputInterface $input, SymfonyStyle $output): int foreach ($options as $version => $params) { $commitsRaw = Repository::getCommits($params['options']); + usort($commitsRaw, function ($x, $y) use ($sortBy, $sortOrientation) { + if (array_key_exists($sortBy, $x)) { + if ($sortOrientation === 'ASC') { + return $x[$sortBy] <=> $y[$sortBy]; + } + + return $y[$sortBy] <=> $x[$sortBy]; + } + + return 0; + }); // Get all commits information $commits = []; @@ -382,6 +398,9 @@ public function generate(InputInterface $input, SymfonyStyle $output): int } } + // Hook post run + $this->config->postRun(); + return Command::SUCCESS; } @@ -410,7 +429,9 @@ protected function getMarkdownChanges(array $changes): string $changelog .= "\n### {$label}\n\n"; ksort($list); foreach ($list as $scope => $items) { - asort($items); + if ($this->config->getSortBy() === 'subject') { + ksort($items); + } if (is_string($scope) && !empty($scope)) { // scope section $changelog .= "\n##### {$scope}\n\n"; diff --git a/src/Configuration.php b/src/Configuration.php index 20b7635..c8a26af 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -194,6 +194,41 @@ class Configuration */ protected $prettyScope = true; + /** + * Sort by options and orientation. + */ + protected const SORT_BY = [ + 'date' => 'DESC', + 'subject' => 'ASC', + 'authorName' => 'ASC', + 'authorEmail' => 'ASC', + 'authorDate' => 'DESC', + 'committerName' => 'ASC', + 'committerEmail' => 'ASC', + 'committerDate' => 'DESC', + ]; + + /** + * Commit sorting. + * + * @var string + */ + protected $sortBy = 'subject'; + + /** + * Pre run. + * + * @var mixed + */ + protected $preRun; + + /** + * Post run. + * + * @var mixed + */ + protected $postRun; + /** * Constructor. */ @@ -229,6 +264,7 @@ public function fromArray(array $array) 'root' => null, 'headerTitle' => $this->getHeaderTitle(), 'headerDescription' => $this->getHeaderDescription(), + 'sortBy' => $this->getSortBy(), 'path' => $this->getPath(), 'preset' => $this->getPreset(), 'types' => [], @@ -249,6 +285,8 @@ public function fromArray(array $array) 'issueUrlFormat' => $this->getIssueUrlFormat(), 'userUrlFormat' => $this->getUserUrlFormat(), 'releaseCommitMessageFormat' => $this->getReleaseCommitMessageFormat(), + 'preRun' => $this->getPreRun(), + 'postRun' => $this->getPostRun(), ]; $params = array_replace_recursive($defaults, $array); @@ -272,37 +310,43 @@ public function fromArray(array $array) } // Add breaking changes - $params['preset'] = array_merge($this->getBreakingChangesPreset(), $params['preset']); - - // Paths - $this->setRoot($params['root']); - $this->setPath($params['path']); - // Types - $this->setIgnorePatterns($params['ignorePatterns']); - $this->setIgnoreTypes($params['ignoreTypes']); - $this->setTypes($params['preset']); - // Document - $this->setHeaderTitle($params['headerTitle']); - $this->setHeaderDescription($params['headerDescription']); - // Tag - $this->setTagPrefix($params['tagPrefix']); - $this->setTagSuffix($params['tagSuffix']); - // Skips - $this->setSkipBump($params['skipBump']); - $this->setSkipTag($params['skipTag']); - $this->setSkipVerify($params['skipVerify']); - // Hidden - $this->setHiddenHash($params['hiddenHash']); - $this->setHiddenMentions($params['hiddenMentions']); - $this->setHiddenReferences($params['hiddenReferences']); - // Formats - $this->setPrettyScope($params['prettyScope']); - $this->setUrlProtocol($params['urlProtocol']); - $this->setCommitUrlFormat($params['commitUrlFormat']); - $this->setCompareUrlFormat($params['compareUrlFormat']); - $this->setIssueUrlFormat($params['issueUrlFormat']); - $this->setUserUrlFormat($params['userUrlFormat']); - $this->setReleaseCommitMessageFormat($params['releaseCommitMessageFormat']); + $breakingPreset = $this->getBreakingChangesPreset(); + $params['preset'] = array_merge($breakingPreset, $params['preset']); + + $this + // Paths + ->setRoot($params['root']) + ->setPath($params['path']) + // Types + ->setIgnorePatterns($params['ignorePatterns']) + ->setIgnoreTypes($params['ignoreTypes']) + ->setTypes($params['preset']) + // Document + ->setHeaderTitle($params['headerTitle']) + ->setHeaderDescription($params['headerDescription']) + ->setSortBy($params['sortBy']) + // Tag + ->setTagPrefix($params['tagPrefix']) + ->setTagSuffix($params['tagSuffix']) + // Skips + ->setSkipBump($params['skipBump']) + ->setSkipTag($params['skipTag']) + ->setSkipVerify($params['skipVerify']) + // Hidden + ->setHiddenHash($params['hiddenHash']) + ->setHiddenMentions($params['hiddenMentions']) + ->setHiddenReferences($params['hiddenReferences']) + // Formats + ->setPrettyScope($params['prettyScope']) + ->setUrlProtocol($params['urlProtocol']) + ->setCommitUrlFormat($params['commitUrlFormat']) + ->setCompareUrlFormat($params['compareUrlFormat']) + ->setIssueUrlFormat($params['issueUrlFormat']) + ->setUserUrlFormat($params['userUrlFormat']) + ->setReleaseCommitMessageFormat($params['releaseCommitMessageFormat']) + // Hooks + ->setPreRun($params['preRun']) + ->setPostRun($params['postRun']); } /** @@ -664,4 +708,89 @@ public function setPrettyScope(bool $prettyScope): self return $this; } + + public function getSortBy(): string + { + $sortBy = $this->sortBy; + if ($sortBy === 'date') { + $sortBy = 'committerDate'; + } + + return $sortBy; + } + + public function setSortBy(string $sortBy): self + { + $sortBy = trim($sortBy); + $keys = array_keys(self::SORT_BY); + if (!in_array($sortBy, $keys)) { + $sortBy = 'date'; + } + $this->sortBy = $sortBy; + + return $this; + } + + public function getSortOrientation(string $sort): string + { + return self::SORT_BY[$sort]; + } + + /** + * @param mixed $hook + */ + public function runHook($hook) + { + if (is_string($hook)) { + system($hook); + } elseif (is_callable($hook)) { + call_user_func($hook); + } + } + + /** + * @return mixed + */ + public function getPreRun() + { + return $this->preRun; + } + + public function preRun() + { + $this->runHook($this->preRun); + } + + /** + * @param mixed $preRun + */ + public function setPreRun($preRun): self + { + $this->preRun = $preRun; + + return $this; + } + + /** + * @return mixed + */ + public function getPostRun() + { + return $this->postRun; + } + + public function postRun() + { + $this->runHook($this->preRun); + } + + /** + * @param mixed $postRun + */ + public function setPostRun($postRun): self + { + $this->postRun = $postRun; + + return $this; + } }