Skip to content

Commit

Permalink
feat: add and implementat configuration moving default values from ch…
Browse files Browse the repository at this point in the history
…angelog class to it
  • Loading branch information
marcocesarato committed Jan 17, 2021
1 parent 1870d3a commit 28001b4
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 48 deletions.
77 changes: 32 additions & 45 deletions src/Changelog.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,17 @@
class Changelog
{
/**
* Changelog filename.
*
* @var string
*/
public static $fileName = 'CHANGELOG.md';

/**
* Types allowed on changelog and labels (preserve the order).
*
* @var string[][]
*/
public static $types = [
'feat' => ['code' => 'feat', 'label' => 'Features'],
'perf' => ['code' => 'perf', 'label' => 'Performance Features'],
'fix' => ['code' => 'fix', 'label' => 'Fixes'],
'refactor' => ['code' => 'refactor', 'label' => 'Refactoring'],
'docs' => ['code' => 'docs', 'label' => 'Docs'],
'chore' => ['code' => 'chore', 'label' => 'Chores'],
];

/**
* Changelog pattern.
*
* @var string
* @var Configuration
*/
public static $header = "# Changelog\nAll notable changes to this project will be documented in this file.\n\n\n";
protected $config;

/**
* Ignore message commit patterns.
*
* @var string[]
* Changelog constructor.
*/
public static $ignorePatterns = [
'/^chore\(release\):/i',
];
public function __construct(Configuration $config)
{
$this->config = $config;
}

/**
* Generate changelog.
Expand All @@ -70,24 +46,35 @@ public function generate(InputInterface $input, SymfonyStyle $output)

// Exclude types
if ($excludeChores) {
unset(self::$types['chore']);
unset($this->config->types['chore']);
}
if ($excludeRefactor) {
unset(self::$types['refactor']);
unset($this->config->types['refactor']);
}

// Initialize changelogs
$file = $root . DIRECTORY_SEPARATOR . self::$fileName;
$file = $root . DIRECTORY_SEPARATOR . $this->config->getFileName();
$changelogCurrent = '';
$changelogNew = '';

$mainHeaderPrefix = "<!--- BEGIN HEADER -->\n# ";
$mainHeaderSuffix = "\n<!--- END HEADER -->\n\n";
$mainHeaderContent = $this->config->getHeaderTitle() . "\n\n" . $this->config->getHeaderDescription();
$mainHeader = $mainHeaderPrefix . $mainHeaderContent . $mainHeaderSuffix;

// Get changelogs content
if (file_exists($file)) {
$header = ltrim(self::$header);
$header = preg_quote($header, '/');
$changelogCurrent = file_get_contents($file);
$changelogCurrent = ltrim($changelogCurrent);
$changelogCurrent = preg_replace("/^$header/i", '', $changelogCurrent);

// Remove header
$beginHeader = preg_quote($mainHeaderPrefix, '/');
$endHeader = preg_quote($mainHeaderSuffix, '/');

$pattern = '/^(' . $beginHeader . '(.*)' . $endHeader . ')/si';
$pattern = preg_replace(['/[\n]+/', '/[\s]+/'], ['[\n]+', '[\s]+'], $pattern);

$changelogCurrent = preg_replace($pattern, '', $changelogCurrent);
}

// Current Dates
Expand Down Expand Up @@ -199,7 +186,7 @@ public function generate(InputInterface $input, SymfonyStyle $output)
}
// Check ignored commit
$ignore = false;
foreach (self::$ignorePatterns as $pattern) {
foreach ($this->config->getIgnorePatterns() as $pattern) {
if (preg_match($pattern, $head)) {
$ignore = true;
break;
Expand All @@ -217,24 +204,24 @@ public function generate(InputInterface $input, SymfonyStyle $output)

// Changes groups
$changes = [];
foreach (self::$types as $key => $type) {
foreach ($this->config->getTypes() as $key => $type) {
$changes[$key] = [];
}

// Group all changes to lists by type
foreach ($commits as $commit) {
foreach (self::$types as $key => $type) {
foreach ($this->config->getTypes() as $name => $type) {
$head = Utils::clean($commit['head']);
$code = preg_quote($type['code'], '/');
$code = preg_quote($name, '/');
if (preg_match('/^' . $code . '(\(.*?\))?[:]?\\s/i', $head)) {
$parse = $this->parseCommitHead($head, $type['code']);
$parse = $this->parseCommitHead($head, $name);
$scope = $parse['scope'];
$description = $parse['description'];
$sha = $commit['sha'];
$short = substr($sha, 0, 6);
// List item
$itemKey = strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '', $description));
$changes[$key][$scope][$itemKey][$sha] = [
$changes[$name][$scope][$itemKey][$sha] = [
'description' => $description,
'short' => $short,
'url' => $url,
Expand All @@ -251,7 +238,7 @@ public function generate(InputInterface $input, SymfonyStyle $output)
}

// Save new changelog prepending the current one
file_put_contents($file, self::$header . "{$changelogNew}{$changelogCurrent}");
file_put_contents($file, $mainHeader . "{$changelogNew}{$changelogCurrent}");
$output->success("Changelog generated to: {$file}");

// Create commit and add tag
Expand Down Expand Up @@ -281,7 +268,7 @@ protected function getMarkdownChanges($changes)
continue;
}
ksort($list);
$changelog .= PHP_EOL . '### ' . self::$types[$type]['label'] . "\n\n";
$changelog .= PHP_EOL . '### ' . $this->config->getTypeLabel($type) . "\n\n";
foreach ($list as $scope => $items) {
asort($items);
if (is_string($scope) && !empty($scope)) {
Expand Down
191 changes: 191 additions & 0 deletions src/Configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<?php

namespace ConventionalChangelog;

class Configuration
{
/**
* Changelog filename.
*
* @var string
*/
public $fileName = 'CHANGELOG.md';

/**
* Header description.
*
* @var string
*/
public $headerTitle = 'Changelog';

/**
* Header title.
*
* @var string
*/
public $headerDescription = 'All notable changes to this project will be documented in this file.';

/**
* Types allowed on changelog and labels (preserve the order).
*
* @var string[][]
*/
public $types = [
'feat' => ['label' => 'Features'],
'perf' => ['label' => 'Performance Features'],
'fix' => ['label' => 'Fixes'],
'refactor' => ['label' => 'Refactoring'],
'docs' => ['label' => 'Docs'],
'chore' => ['label' => 'Chores'],
];

/**
* Ignore message commit patterns.
*
* @var string[]
*/
public $ignorePatterns = [
'/^chore\(release\):/i',
];

/**
* Constructor.
*/
public function __construct(array $settings = [])
{
$this->fromArray($settings);
}

/**
* From array.
*
* @param $array
*/
public function fromArray(array $array)
{
if (empty($array)) {
return;
}

$defaults = [
'types' => $this->types,
'excludedTypes' => [],
'headerTitle' => $this->headerTitle,
'headerDescription' => $this->headerDescription,
'fileName' => $this->fileName,
'ignorePatterns' => $this->ignorePatterns,
];

$params = array_replace_recursive($defaults, $array);

if (is_array($params['excludedTypes'])) {
foreach ($params['excludedTypes'] as $type) {
unset($params['types'][$type]);
}
}

foreach ($params['ignorePatterns'] as $key => $pattern) {
if (!$this->isRegex($pattern)) {
$params['ignorePatterns'][$key] = '#' . preg_quote($pattern, '#') . '#i';
}
}

$this->setTypes($params['types']);
$this->setHeaderTitle($params['headerTitle']);
$this->setHeaderDescription($params['headerDescription']);
$this->setFileName($params['fileName']);
$this->setIgnorePatterns($params['ignorePatterns']);
}

/**
* Check if is regex.
*
* @param $pattern
*
* @return bool
*/
protected function isRegex(string $pattern)
{
return @preg_match($pattern, null) !== false;
}

/**
* @return string[][]
*/
public function getTypes(): array
{
return $this->types;
}

/**
* @param $type
*/
public function getTypeLabel(string $type): string
{
return $this->types[$type]['label'];
}

/**
* @param string[][] $types
*/
public function setTypes(array $types): Configuration
{
$this->types = $types;

return $this;
}

public function getFileName(): string
{
return $this->fileName;
}

public function setFileName(string $fileName): Configuration
{
$this->fileName = $fileName;

return $this;
}

public function getHeaderTitle(): string
{
return $this->headerTitle;
}

public function setHeaderTitle(string $headerTitle): Configuration
{
$this->headerTitle = $headerTitle;

return $this;
}

public function getHeaderDescription(): string
{
return $this->headerDescription;
}

public function setHeaderDescription(string $headerDescription): Configuration
{
$this->headerDescription = $headerDescription;

return $this;
}

/**
* @return string[]
*/
public function getIgnorePatterns(): array
{
return $this->ignorePatterns;
}

/**
* @param string[] $ignorePatterns
*/
public function setIgnorePatterns(array $ignorePatterns): Configuration
{
$this->ignorePatterns = $ignorePatterns;

return $this;
}
}
29 changes: 26 additions & 3 deletions src/DefaultCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,31 @@

class DefaultCommand extends Command
{
/**
* Command name.
*
* @var string
*/
protected static $defaultName = 'changelog';

/**
* Changelog.
*
* @var Changelog
*/
public $changelog;

/**
* Constructor.
*/
public function __construct(array $settings = [])
{
parent::__construct(self::$defaultName);

$config = new Configuration($settings);
$this->changelog = new Changelog($config);
}

/**
* Configure.
*
Expand All @@ -19,7 +44,6 @@ class DefaultCommand extends Command
protected function configure()
{
$this
->setName('changelog')
->setDescription('Generate changelogs and release notes from a project\'s commit messages' .
'and metadata and automate versioning with semver.org and conventionalcommits.org')
->setDefinition([
Expand All @@ -45,9 +69,8 @@ protected function configure()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$changelog = new Changelog();
$outputStyle = new SymfonyStyle($input, $output);

return $changelog->generate($input, $outputStyle);
return $this->changelog->generate($input, $outputStyle);
}
}

0 comments on commit 28001b4

Please sign in to comment.