Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Cli/ModuleProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Horde\Cli\Modular\Module;
use Horde\Cli\Modular\Modules;
use Horde\Components\Module\Composer;
use Horde\Components\Module\ConventionalCommit;
use Horde\Components\Module\Change;
use Horde\Components\Module\InstallModule;
use Horde\Components\Module\Package;
Expand Down Expand Up @@ -48,6 +49,7 @@ public function getModules(): Modules
$this->injector->get(ConfigModule::class),
$this->injector->get(Composer::class),
$this->injector->get(Git::class),
$this->injector->get(ConventionalCommit::class),
$this->injector->get(Help::class),
$this->injector->get(InstallModule::class),
$this->injector->get(Package::class),
Expand Down
31 changes: 30 additions & 1 deletion src/ConventionalCommit.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class ConventionalCommit extends GitCommit
public readonly string $description;
public readonly string $scope;
public readonly string $type;
public readonly string $stability;

public function __construct(
string $commit = '',
Expand All @@ -33,6 +34,7 @@ public function __construct(
string $committer_date='',
string $trailers='',
array $conventionalAttributes=[],
string $stability = 'unchanged'
) {
parent::__construct(
commit: $commit,
Expand Down Expand Up @@ -82,6 +84,11 @@ public function __construct(
if ($severity === 'major') {
$breaking = true;
}
// Prefer stability from explicit parameter unless it's "unchanged" and conventionalAttributes disagrees.
if (array_key_exists('stability', $conventionalAttributes) && $stability === 'unchanged') {
$stability = $conventionalAttributes['stability'];
}
$this->stability = $stability;
$this->breaking = $breaking;
$this->severity = $severity;
$this->scope = rtrim(ltrim((string)($conventionalAttributes['scope'] ?? ''), "("), ")");
Expand All @@ -94,9 +101,31 @@ public static function fromGitCommit(GitCommit $commit): ConventionalCommit|null
{
$regex = '/^(?P<type>build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test){1}(?P<scope>\([\w\-\.]+\))?(?P<breaking>!)?: (?P<description>.*)\s*/u';
$res = preg_match($regex, $commit->subject, $matches);
if ($res == 0){
if ($res == 0) {
return null;
}
// Handle BREAKING CHANGE: Description (from ConventionalCommit) and INCOMPATBLE: Description (from AutoSemVer)
$breakingFooterRegex = '/^\s*(?P<breaking>BREAKING\s+CHANGE|INCOMPATIBLE):\s+(?P<breaking_description>.+)/u';
$bodyLines = explode("\n", $commit->body);
foreach ($bodyLines as $line) {
$res = preg_match($breakingFooterRegex, $line, $breakingMatches);
if (!empty($breakingMatches['breaking'])) {
$matches['breaking'] = '!';
$matches['breaking_description'] = $breakingMatches['breaking_description'];
break;
}
}
// Handle AutoSemver inspired stability footer
$stabilityFooterRegex = '/^\s*(?P<stability>STABILITY|STABILITY\s+CHANGE):\s+(?P<new_stability>.+)/u';
$bodyLines = explode("\n", $commit->body);
foreach ($bodyLines as $line) {
$res = preg_match($stabilityFooterRegex, $line, $stabilityMatches);
if (!empty($stabilityMatches['new_stability'])) {
$matches['stability'] = $stabilityMatches['new_stability'];
break;
}
}

return new ConventionalCommit(
conventionalAttributes: $matches,
commit: $commit->commit,
Expand Down
12 changes: 12 additions & 0 deletions src/ConventionalCommitReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ final class ConventionalCommitReader
{
private GitCommitLog $log;
private string $topSeverity = 'subpatch';
private string $stability = 'unchanged';
public function __construct(
GitCommitLog $log,
) {
Expand All @@ -32,6 +33,13 @@ private function readConventionalCommits(GitCommitLog $log): void
$conventionalCommits[] = $conventionalCommit;
}
}
// Get the latest stability-changing commit
foreach ($conventionalCommits as $commit) {
if ($commit->stability !== 'unchanged') {
$this->stability = $commit->stability;
}
break;
}
// TODO: Handle trailers
// Save only conventional commits
$this->log = new GitCommitLog(...$conventionalCommits);
Expand All @@ -54,6 +62,10 @@ public function getTopSeverity(): string
{
return $this->topSeverity;
}
public function getLatestStabilityChange(): string
{
return $this->stability;
}
public function getLog(): GitCommitLog
{
return $this->log;
Expand Down
5 changes: 5 additions & 0 deletions src/Dependencies/Injector.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Horde\Components\Release\Notes as ReleaseNotes;
use Horde\Components\Release\Tasks as ReleaseTasks;
use Horde\Components\Runner\Change as RunnerChange;
use Horde\Components\Runner\ConventionalCommit as RunnerConventionalCommit;
use Horde\Components\Runner\CiPrebuild as RunnerCiPrebuild;
use Horde\Components\Runner\CiSetup as RunnerCiSetup;
use Horde\Components\Runner\Composer as RunnerComposer;
Expand Down Expand Up @@ -251,6 +252,10 @@ public function getRunnerChange()
{
return $this->getInstance(RunnerChange::class);
}
public function getRunnerConventionalCommit()
{
return $this->getInstance(RunnerConventionalCommit::class);
}

/**
* Returns the snapshot packaging handler for a package.
Expand Down
34 changes: 26 additions & 8 deletions src/Helper/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ public function getOriginal(): string
public function nextVersionObject(string $severity='patch', string $stability='unchanged'): Version
{
$nextVersion = clone $this;
$nextVersion->original = '';
$newStability = $this->normalizeStability($stability);
$stabilityChangeDirection = $this->stabilityChangeDirection($newStability);
// unstable target versions
Expand Down Expand Up @@ -206,24 +207,41 @@ public function nextVersionObject(string $severity='patch', string $stability='u
return $nextVersion;
}

public static function isUnstable(): bool
public static function isUnstable(string $stability): bool
{
return $stability !== 'stable' && $stability !== '';
}
public static function isStable(): bool
public static function isStable(string $stability): bool
{
return $this->stability === 'stable' || $this->stability === '';
return $tability === 'stable' || $stability === '';
}

public static function isUp(string $stabilityChangeDirection): bool
public static function isUp(int $stabilityChangeDirection): bool
{
return $stabilityChangeDirection > 0;
}
public static function isDown(string $stabilityChangeDirection): bool
public static function isDown(int $stabilityChangeDirection): bool
{
return $stabilityChangeDirection < 0;
}

public function toHordeTag(): string
{
$version = 'v' . $this->getMajor() . '.' . $this->getMinor() . '.' . $this->getPatch();
$subpatch = $this->getSubPatch();
if ((int)$subpatch > 0) {
$version .= '.' . $subpatch;
}
$stability = $this->getStability();
if ($stability) {
$version .= $stability . $this->getStabilityVersion();
}
if ($this->getBuildInfo()) {
'+' . $this->getBuildInfo();
}
return $version;
}

public function normalizeStability(string $stability): string
{
if ($stability === 'unchanged') {
Expand All @@ -234,7 +252,7 @@ public function normalizeStability(string $stability): string
$stability = '';
} elseif ($stability === 'unstable') {
$stability = 'alpha';
} elseif ($newStability === 'devel') {
} elseif ($stability === 'devel') {
$stability = 'dev';
}
return $stability;
Expand Down Expand Up @@ -283,10 +301,10 @@ public static function fromComposerString(string $version): Version
}
// Parse stability and stability integer, stripping leading hyphen if any
ltrim($stability, '-');
$res = preg_match('/^(\w+)(\d+)?$/', $stability, $stabilityMatch);
$res = preg_match('/^([A-Za-z]+)(\d+)?$/', $stability, $stabilityMatch);
$stability = $stabilityMatch[1] ?? '';
if ($stability) {
$stabilityVersion = $stabilityMatch[2] ?? 1;
$stabilityVersion = (int)$stabilityMatch[2] ?? 1;
} else {
$stabilityVersion = 0;
}
Expand Down
141 changes: 141 additions & 0 deletions src/Module/ConventionalCommit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php
/**
* Components\Module\ConventionalCommit:: Handle conventional commits.
*
* PHP Version 8
*
* @category Horde
* @package Components
* @author Ralf Lang <[email protected]>
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
*/

namespace Horde\Components\Module;

use Horde\Components\Config;
use Horde\Components\Dependencies;
use Horde\Components\Component\ComponentDirectory;
use Horde\Components\RuntimeContext\CurrentWorkingDirectory;

/**
* Components_Module_Change:: records a change log entry.
*
* Copyright 2011-2024 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @category Horde
* @package Components
* @author Ralf Lang <[email protected]>
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
*/
class ConventionalCommit extends Base
{
public function __construct(Dependencies $dependencies)
{
parent::__construct($dependencies);
}


public function getOptionGroupTitle(): string
{
return 'Conventional Commit';
}

public function getOptionGroupDescription(): string
{
return 'Extracts changelog and version information from git logs in Conventional Commit format.';
}

public function getOptionGroupOptions(): array
{
return [];
}

/**
* Get the usage title for this module.
*
* @return string The title.
*/
public function getTitle(): string
{
return 'conventionalcommit';
}

/**
* Get the usage description for this module.
*
* @return string The description.
*/
public function getUsage(): string
{
return "[show|lastversion|nextversion] [--since=tag] - Read from git log";
}

/**
* Return the action arguments supported by this module.
*
* @return array A list of supported action arguments.
*/
public function getActions(): array
{
return ['conventionalcommit'];
}

/**
* Return the help text for the specified action.
*
* @param string $action The action.
*
* @return string The help text.
*/
public function getHelp($action): string
{
return "
conventionalcommit [show] [--since=tag] - Summary of changes.
conventionalcommit lastversion - Show the last version as of git tags.
conventionalcommit
conventionalcommit nextversion - Show the next version as of git tags.";
}

/**
* Return the options that should be explained in the context help.
*
* @return array A list of option help texts.
*/
public function getContextOptionHelp(): array
{
return [
//'--commit' => 'Commit the change log entries to git (using the change log entry as commit message).', '--pretend' => ''
//
];
}

/**
* Determine if this module should act. Run all required actions if it has
* been instructed to do so.
*
* @param Config $config The configuration.
*
* @return bool True if the module performed some action.
*/
public function handle(Config $config): bool
{
$options = $config->getOptions();
$arguments = $config->getArguments();

if (!empty($options['conventionalcommit']) ||
(isset($arguments[0]) && $arguments[0] == 'conventionalcommit')) {
$componentDirectory = new ComponentDirectory($options['working_dir'] ?? new CurrentWorkingDirectory);
$component = $this->dependencies
->getComponentFactory()
->createSource($componentDirectory);
$config->setComponent($component);

$this->dependencies->getRunnerConventionalCommit()->run($config);
return true;
}
return false;
}
}
Loading
Loading