From 48b4e089b6a5728854c4d9707034f385d182dc8a Mon Sep 17 00:00:00 2001
From: Thoriq Firdaus <2067467+tfirdaus@users.noreply.github.com>
Date: Mon, 28 Jul 2025 08:39:44 +0700
Subject: [PATCH 1/6] Add increment command
---
app/Commander.php | 2 +
app/Commands/IncrementCommand.php | 90 ++++++++++++++++++++++++++
app/Commands/ValidateCommand.php | 6 +-
app/Exceptions/InvalidArgumentType.php | 19 ++++++
4 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 app/Commands/IncrementCommand.php
create mode 100644 app/Exceptions/InvalidArgumentType.php
diff --git a/app/Commander.php b/app/Commander.php
index 73a566a..a27b5f1 100644
--- a/app/Commander.php
+++ b/app/Commander.php
@@ -6,6 +6,7 @@
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
+use Syntatis\Version\CLI\Commands\IncrementCommand;
use Syntatis\Version\CLI\Commands\ValidateCommand;
final class Commander extends Application
@@ -26,6 +27,7 @@ public function __construct()
private function getCommands(): array
{
return [
+ new IncrementCommand(),
new ValidateCommand(),
];
}
diff --git a/app/Commands/IncrementCommand.php b/app/Commands/IncrementCommand.php
new file mode 100644
index 0000000..d7bbcf3
--- /dev/null
+++ b/app/Commands/IncrementCommand.php
@@ -0,0 +1,90 @@
+setName('increment');
+ $this->setDescription('Increment a version.');
+ $this->setHelp('This command increments the provided version by the specified part (major, minor, patch).');
+ $this->setAliases(['incr']);
+ $this->addArgument('part', InputArgument::REQUIRED, 'Part to increment (major, minor, patch)');
+ $this->addArgument('version', InputArgument::REQUIRED, 'Version to increment');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $style = new SymfonyStyle($input, $output);
+ $part = $input->getArgument('part');
+ $version = $input->getArgument('version');
+
+ try {
+ if (! is_string($part)) {
+ throw new InvalidArgumentType($part);
+ }
+ } catch (Throwable $th) {
+ $style->error($th->getMessage());
+
+ return Command::FAILURE;
+ }
+
+ try {
+ if (! is_string($version)) {
+ throw new InvalidArgumentType($version);
+ }
+
+ /** @var Version $parsed */
+ $parsed = Version::fromString($version);
+
+ $style->writeln(
+ (string) $this->increment(
+ $parsed,
+ $part,
+ ),
+ );
+ } catch (Throwable $th) {
+ $style->error($th->getMessage());
+
+ return Command::FAILURE;
+ }
+
+ return Command::SUCCESS;
+ }
+
+ private function increment(Version $version, string $part): Version
+ {
+ switch ($part) {
+ case 'major':
+ return $version->incrementMajor();
+
+ case 'minor':
+ return $version->incrementMinor();
+
+ case 'patch':
+ return $version->incrementPatch();
+
+ default:
+ throw new InvalidArgumentException(sprintf("Invalid part '%s' provided. Expected 'major', 'minor', or 'patch'.", $part));
+ }
+ }
+}
diff --git a/app/Commands/ValidateCommand.php b/app/Commands/ValidateCommand.php
index ae48ae9..e286b1d 100644
--- a/app/Commands/ValidateCommand.php
+++ b/app/Commands/ValidateCommand.php
@@ -9,11 +9,10 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
+use Syntatis\Version\CLI\Concerns\InvalidArgumentType;
use Throwable;
-use TypeError;
use Version\Version;
-use function gettype;
use function is_string;
use function sprintf;
@@ -39,12 +38,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
try {
if (is_string($version)) {
Version::fromString($version);
+
$style->success(sprintf("Version string '%s' is valid and can be parsed", $version));
return Command::SUCCESS;
}
- throw new TypeError(sprintf("Invalid type of value to validate. Expected string, '%s' given", gettype($version)));
+ throw new InvalidArgumentType($version);
} catch (Throwable $th) {
$style->error($th->getMessage());
diff --git a/app/Exceptions/InvalidArgumentType.php b/app/Exceptions/InvalidArgumentType.php
new file mode 100644
index 0000000..0132414
--- /dev/null
+++ b/app/Exceptions/InvalidArgumentType.php
@@ -0,0 +1,19 @@
+
Date: Mon, 28 Jul 2025 10:10:27 +0700
Subject: [PATCH 2/6] Update `increment` command
---
app/Commands/IncrementCommand.php | 48 ++++++++++--
composer.json | 4 +-
tests/app/Commands/IncrementCommandTest.php | 82 +++++++++++++++++++++
3 files changed, 124 insertions(+), 10 deletions(-)
create mode 100644 tests/app/Commands/IncrementCommandTest.php
diff --git a/app/Commands/IncrementCommand.php b/app/Commands/IncrementCommand.php
index d7bbcf3..e79e7cf 100644
--- a/app/Commands/IncrementCommand.php
+++ b/app/Commands/IncrementCommand.php
@@ -27,16 +27,30 @@ protected function configure(): void
$this->setName('increment');
$this->setDescription('Increment a version.');
$this->setHelp('This command increments the provided version by the specified part (major, minor, patch).');
- $this->setAliases(['incr']);
- $this->addArgument('part', InputArgument::REQUIRED, 'Part to increment (major, minor, patch)');
+ $this->setAliases(['incr', 'bump']);
$this->addArgument('version', InputArgument::REQUIRED, 'Version to increment');
+ $this->addOption('part', 'p', InputArgument::OPTIONAL, 'Part to increment (major, minor, patch)', 'patch');
+ $this->addOption('build', 'b', InputArgument::OPTIONAL, 'Build metadata to append to the version');
+ $this->addOption('pre', null, InputArgument::OPTIONAL, 'Pre-release identifier to append to the version');
+ $this->setHelp(<<<'HELP'
+ This command increments the provided version by the specified part (major, minor, patch).
+ You can also append build metadata or a pre-release identifier to the version.
+
+ Usage:
+ version increment 1.0.0
+ version increment 1.0.0 --part=minor
+ version increment 1.0.0 --build=123
+ version increment 1.0.0 --pre=beta
+ HELP,);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$style = new SymfonyStyle($input, $output);
- $part = $input->getArgument('part');
+ $part = $input->getOption('part');
$version = $input->getArgument('version');
+ $build = $input->getOption('build');
+ $pre = $input->getOption('pre');
try {
if (! is_string($part)) {
@@ -55,11 +69,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
/** @var Version $parsed */
$parsed = Version::fromString($version);
-
$style->writeln(
(string) $this->increment(
$parsed,
$part,
+ $pre,
+ $build,
),
);
} catch (Throwable $th) {
@@ -71,20 +86,37 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return Command::SUCCESS;
}
- private function increment(Version $version, string $part): Version
+ /**
+ * @param mixed $pre
+ * @param mixed $build
+ */
+ private function increment(Version $version, string $part, $pre = null, $build = null): Version
{
switch ($part) {
case 'major':
- return $version->incrementMajor();
+ $version = $version->incrementMajor();
+ break;
case 'minor':
- return $version->incrementMinor();
+ $version = $version->incrementMinor();
+ break;
case 'patch':
- return $version->incrementPatch();
+ $version = $version->incrementPatch();
+ break;
default:
throw new InvalidArgumentException(sprintf("Invalid part '%s' provided. Expected 'major', 'minor', or 'patch'.", $part));
}
+
+ if (is_string($pre) && $pre !== '') {
+ $version = $version->withPreRelease($pre);
+ }
+
+ if (is_string($build) && $build !== '') {
+ $version = $version->withBuild($build);
+ }
+
+ return $version;
}
}
diff --git a/composer.json b/composer.json
index e0cc903..8bd65c6 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
- "name": "syntatis/version-cli-php",
- "description": "Increment, compare SemVer-compliant version number with CLI.",
+ "name": "syntatis/version-cli",
+ "description": "Validate, increment, compare, etc. SemVer-compliant version number with CLI",
"keywords": ["version", "cli", "semver"],
"authors": [
{
diff --git a/tests/app/Commands/IncrementCommandTest.php b/tests/app/Commands/IncrementCommandTest.php
new file mode 100644
index 0000000..3f6e1e3
--- /dev/null
+++ b/tests/app/Commands/IncrementCommandTest.php
@@ -0,0 +1,82 @@
+commander = new Commander();
+ }
+
+ /** @dataProvider dataInvalidVersionArgument */
+ public function testInvalidVersionArgument(string $version): void
+ {
+ $tester = new CommandTester($this->commander->get('increment'));
+ $tester->execute(['version' => $version]);
+
+ self::assertStringContainsString(
+ sprintf("[ERROR] Version string '%s' is not valid and cannot be parsed", $version),
+ $tester->getDisplay(),
+ );
+ }
+
+ public function testIncrementPatch(): void
+ {
+ $tester = new CommandTester($this->commander->get('increment'));
+ $tester->execute(['version' => '1.0.0']);
+
+ self::assertStringContainsString('1.0.1', $tester->getDisplay());
+ }
+
+ public function testIncrementMinor(): void
+ {
+ $tester = new CommandTester($this->commander->get('increment'));
+ $tester->execute(['version' => '1.0.0', '--part' => 'minor']);
+
+ self::assertStringContainsString('1.1.0', $tester->getDisplay());
+ }
+
+ public function testIncrementMajor(): void
+ {
+ $tester = new CommandTester($this->commander->get('increment'));
+ $tester->execute(['version' => '1.0.0', '--part' => 'major']);
+
+ self::assertStringContainsString('2.0.0', $tester->getDisplay());
+ }
+
+ public function testIncrementWithBuildMetadata(): void
+ {
+ $tester = new CommandTester($this->commander->get('increment'));
+ $tester->execute(['version' => '1.0.0', '--build' => '123']);
+
+ self::assertStringContainsString('1.0.1+123', $tester->getDisplay());
+ }
+
+ public function testIncrementWithPreRelease(): void
+ {
+ $tester = new CommandTester($this->commander->get('increment'));
+ $tester->execute(['version' => '1.0.0', '--pre' => 'beta']);
+
+ self::assertStringContainsString('1.0.1-beta', $tester->getDisplay());
+ }
+
+ public static function dataInvalidVersionArgument(): iterable
+ {
+ yield ['v'];
+ yield ['v0'];
+ yield ['v0.0'];
+ }
+}
From d596435260c03902407365b34ac101960e03819f Mon Sep 17 00:00:00 2001
From: Thoriq Firdaus <2067467+tfirdaus@users.noreply.github.com>
Date: Mon, 28 Jul 2025 10:13:26 +0700
Subject: [PATCH 3/6] Update package version
---
.github/workflows/ci.yml | 12 +++---------
composer.json | 4 ++--
2 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a45bb54..9e9532f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,6 @@ concurrency:
cancel-in-progress: true
jobs:
-
sniff:
runs-on: ubuntu-latest
name: Sniff
@@ -68,24 +67,19 @@ jobs:
fail-fast: true
max-parallel: 2
matrix:
- php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
+ php: ["7.4", "8.0", "8.1", "8.2", "8.3", "8.4"]
steps:
- name: Checkout code
uses: actions/checkout@v2
- - name: Setup PHP for dependencies
+ - name: Setup PHP for test
uses: shivammathur/setup-php@v2
with:
- php-version: 7.4
+ php-version: ${{ matrix.php }}
- name: Install PHP dependencies
uses: ramsey/composer-install@v3
- - name: Setup PHP for test
- uses: shivammathur/setup-php@v2
- with:
- php-version: ${{ matrix.php }}
-
- name: Run test
run: composer test
diff --git a/composer.json b/composer.json
index 8bd65c6..3ccd190 100644
--- a/composer.json
+++ b/composer.json
@@ -20,8 +20,8 @@
},
"require": {
"php": "^7.4 || ^8.0",
- "nikolaposa/version": "^4.1",
- "symfony/console": "^5.4"
+ "nikolaposa/version": "^4.1.1",
+ "symfony/console": "^5.4 || ^6.0 || ^7.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
From 23156001bbc08e1eafb7a47bd3f372ce4ec64314 Mon Sep 17 00:00:00 2001
From: Thoriq Firdaus <2067467+tfirdaus@users.noreply.github.com>
Date: Mon, 28 Jul 2025 10:13:54 +0700
Subject: [PATCH 4/6] Update maximum parallel
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9e9532f..29a8bd0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -65,7 +65,7 @@ jobs:
strategy:
fail-fast: true
- max-parallel: 2
+ max-parallel: 3
matrix:
php: ["7.4", "8.0", "8.1", "8.2", "8.3", "8.4"]
From f2cae214c06a42b38ad0d70fe34a4129e01a6e3e Mon Sep 17 00:00:00 2001
From: Thoriq Firdaus <2067467+tfirdaus@users.noreply.github.com>
Date: Mon, 28 Jul 2025 10:33:18 +0700
Subject: [PATCH 5/6] Fix import argument namespace
---
app/Commands/ValidateCommand.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/Commands/ValidateCommand.php b/app/Commands/ValidateCommand.php
index e286b1d..f76b65a 100644
--- a/app/Commands/ValidateCommand.php
+++ b/app/Commands/ValidateCommand.php
@@ -9,7 +9,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
-use Syntatis\Version\CLI\Concerns\InvalidArgumentType;
+use Syntatis\Version\CLI\Exceptions\InvalidArgumentType;
use Throwable;
use Version\Version;
From 5989cdbf3fcecf256f83c099415cfab69af059d8 Mon Sep 17 00:00:00 2001
From: Thoriq Firdaus <2067467+tfirdaus@users.noreply.github.com>
Date: Mon, 28 Jul 2025 10:37:29 +0700
Subject: [PATCH 6/6] Update description
---
composer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index 3ccd190..ee16d62 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "syntatis/version-cli",
- "description": "Validate, increment, compare, etc. SemVer-compliant version number with CLI",
+ "description": "Validate, increment, and compare SemVer-compliant version number with CLI",
"keywords": ["version", "cli", "semver"],
"authors": [
{