diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a45bb54..29a8bd0 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
@@ -66,26 +65,21 @@ 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']
+ 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/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..e79e7cf
--- /dev/null
+++ b/app/Commands/IncrementCommand.php
@@ -0,0 +1,122 @@
+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', '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->getOption('part');
+ $version = $input->getArgument('version');
+ $build = $input->getOption('build');
+ $pre = $input->getOption('pre');
+
+ 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,
+ $pre,
+ $build,
+ ),
+ );
+ } catch (Throwable $th) {
+ $style->error($th->getMessage());
+
+ return Command::FAILURE;
+ }
+
+ return Command::SUCCESS;
+ }
+
+ /**
+ * @param mixed $pre
+ * @param mixed $build
+ */
+ private function increment(Version $version, string $part, $pre = null, $build = null): Version
+ {
+ switch ($part) {
+ case 'major':
+ $version = $version->incrementMajor();
+ break;
+
+ case 'minor':
+ $version = $version->incrementMinor();
+ break;
+
+ case 'patch':
+ $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/app/Commands/ValidateCommand.php b/app/Commands/ValidateCommand.php
index ae48ae9..f76b65a 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\Exceptions\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 @@
+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'];
+ }
+}