Skip to content

Commit

Permalink
feat: add breaking changes and issues references
Browse files Browse the repository at this point in the history
  • Loading branch information
marcocesarato committed Jan 19, 2021
1 parent d644210 commit bda5458
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 26 deletions.
2 changes: 0 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# TODO

- Add `⚠ BREAKING CHANGES` list on top of the version release changes
- Add `Refs` and `Closes` on changes line
- Automated bump version (if not specified version bump)
- Major if find `BREAKING CHANGES`
- Minor if find `feat`
Expand Down
63 changes: 46 additions & 17 deletions src/Changelog.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,6 @@ public function generate(InputInterface $input, SymfonyStyle $output): int
$newVersion = '1.0.0';
}

// Remote url
$url = Git::getRemoteUrl();

$options = []; // Git retrieve options per version

if ($history) {
Expand Down Expand Up @@ -215,24 +212,30 @@ public function generate(InputInterface $input, SymfonyStyle $output): int
}
}

// Changes groups sorting
$changes = ['breaking_changes' => []];
foreach ($this->config->getTypes() as $key => $type) {
$changes[$key] = [];
}

// Group all changes to lists by type
$changes = [];
$types = $this->config->getTypes();
foreach ($commits as $commit) {
if (in_array($commit->getType(), $types)) {
$itemKey = strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '', $commit->getDescription()));
$breakingChanges = $commit->getBreakingChanges();
$type = (string)$commit->getType();
$scope = $commit->getScope()->toPrettyString();
$hash = $commit->getHash();
$changes[$type][$scope][$itemKey][$hash] = [
'description' => ucfirst($commit->getDescription()),
'short' => $commit->getShortHash(),
'url' => $url,
'sha' => $hash,
];
if (!empty($breakingChanges)) {
$type = 'breaking_changes';
}
$changes[$type][$scope][$itemKey][$hash] = $commit;
}
}

// Remote url
$url = Git::getRemoteUrl();
// Initialize changelogs
$changelogNew .= "## [{$params['to']}]($url/compare/{$params['from']}...v{$params['to']}) ({$params['date']})\n\n";
// Add all changes list to new changelog
Expand Down Expand Up @@ -272,42 +275,68 @@ public function generate(InputInterface $input, SymfonyStyle $output): int

/**
* Generate markdown from changes.
*
* @param Commit\Parser[][][][] $changes
*/
protected function getMarkdownChanges(array $changes): string
{
$changelog = '';
// Remote url
$url = Git::getRemoteUrl();
// Add all changes list to new changelog
foreach ($changes as $type => $list) {
if (empty($list)) {
continue;
}
if ($type === 'breaking_changes') {
$label = '⚠ BREAKING CHANGES';
} else {
$label = $this->config->getTypeLabel($type);
}
$changelog .= "\n### {$label}\n\n";
ksort($list);
$changelog .= PHP_EOL . '### ' . $this->config->getTypeLabel($type) . "\n\n";
foreach ($list as $scope => $items) {
asort($items);
if (is_string($scope) && !empty($scope)) {
// scope section
$changelog .= PHP_EOL . "##### {$scope}" . "\n\n";
$changelog .= "\n##### {$scope}\n\n";
}
foreach ($items as $itemsList) {
$important = '';
$description = '';
$sha = '';
$references = '';
$shaGroup = [];
$refsGroup = [];
foreach ($itemsList as $item) {
$description = $item['description'];
if (!empty($item['sha'])) {
$shaGroup[] = "[{$item['short']}]({$item['url']}/commit/{$item['sha']})";
$description = ucfirst($item->getDescription());
$refs = $item->getReferences();

if ($item->isImportant()) {
$important = '**';
}

if (!empty($refs)) {
foreach ($refs as $ref) {
$refsGroup[] = '[#' . $ref . "]({$url}/issue/{$ref})";
}
}
if (!empty($item->getHash())) {
$shaGroup[] = '[' . $item->getShortHash() . "]({$url}/commit/" . $item->getHash() . ')';
}
}
if (!empty($refsGroup)) {
$references = implode(', ', $refsGroup);
}
if (!empty($shaGroup)) {
$sha = '(' . implode(', ', $shaGroup) . ')';
}
$changelog .= "* {$description} {$sha}\n";
$changelog .= "* {$important}{$description}{$important} {$references} {$sha}\n";
}
}
}
// Add version separator
$changelog .= PHP_EOL . '---' . "\n\n";
$changelog .= "\n---\n\n";

return $changelog;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Commit/Body.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ public function __construct(string $content)

public function __toString(): string
{
return $this->content;
return ucfirst($this->content);
}
}
30 changes: 30 additions & 0 deletions src/Commit/Footer.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,36 @@ public function __construct(string $token, string $value)
$this->value = $value;
}

public function getToken(): string
{
return strtolower($this->token);
}

public function getValue(): string
{
return ucfirst($this->value);
}

/**
* Get issues references.
*/
public function getReferences(): array
{
$refs = [];
$value = $this->getValue();
if ($value[0] === '#') {
$values = explode(' ', $value);
foreach ($values as $val) {
$ref = ltrim($val, '#');
if (is_numeric($ref)) {
$refs[] = $ref;
}
}
}

return array_unique($refs);
}

public function __toString(): string
{
return $this->token . ': ' . $this->value;
Expand Down
38 changes: 32 additions & 6 deletions src/Commit/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
class Parser implements Stringable
{
protected const PATTERN_HEADER = "/^(?'type'[a-z]+)(\((?'scope'.+)\))?(?'important'[!]?)[:][[:blank:]](?'description'.+)/iums";
protected const PATTERN_FOOTER = "/(?'token'^([a-z0-9_-]+|BREAKING[[:blank:]]CHANGES?))(?'value'([:][[:blank:]]|[[:blank:]]\#(?=\w)).*?)$/iums";
protected const PATTERN_FOOTER = "/(?'token'^([a-z0-9_-]+|BREAKING[[:blank:]]CHANGES?))(?'value'([:][[:blank:]]|[:]?[[:blank:]][#](?=\w)).*?)$/iums";

/**
* Raw content.
Expand Down Expand Up @@ -94,10 +94,10 @@ public function __construct(string $commit)
protected function parseHeader(string $header)
{
preg_match(self::PATTERN_HEADER, $header, $matches);
$this->type = new Type($matches['type']);
$this->scope = new Scope($matches['scope']);
$this->type = new Type((string)$matches['type']);
$this->scope = new Scope((string)$matches['scope']);
$this->important = !empty($matches['important']) ? true : false;
$this->description = new Description($matches['description']);
$this->description = new Description((string)$matches['description']);
}

/**
Expand All @@ -110,8 +110,9 @@ protected function parseMessage(string $message)
foreach ($matches as $match) {
$footer = $match[0];
$body = str_replace($footer, '', $body);
$value = ltrim($match['value'], ':');
$this->footers[] = new Footer($match['token'], $value);
$value = ltrim((string)$match['value'], ':');
$value = Format::clean($value);
$this->footers[] = new Footer((string)$match['token'], $value);
}
}
$body = Format::clean($body);
Expand Down Expand Up @@ -171,6 +172,31 @@ public function getFooters(): array
return $this->footers;
}

public function getBreakingChanges(): array
{
$messages = [];
foreach ($this->footers as $footer) {
if ($footer->getToken() === 'breaking changes') {
$messages[] = $footer->getValue();
}
}

return $messages;
}

/**
* Get issues references.
*/
public function getReferences(): array
{
$refs = [];
foreach ($this->footers as $footer) {
$refs = array_merge($footer->getReferences());
}

return array_unique($refs);
}

public function getHeader()
{
$header = $this->type;
Expand Down

0 comments on commit bda5458

Please sign in to comment.