Skip to content

Commit

Permalink
Refactored code, added to use default composer json and lock files if…
Browse files Browse the repository at this point in the history
… possible (#4)

Co-authored-by: Martins Rucevskis <[email protected]>
  • Loading branch information
MartinsRucevskis and Martins Rucevskis authored Nov 15, 2023
1 parent a80589c commit e79daaa
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 120 deletions.
53 changes: 53 additions & 0 deletions src/ComposerJsonFromLockBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace MartinsR\ComposerConstraintUpdater;

class ComposerJsonFromLockBuilder
{
public function __construct(
private readonly string $composerJsonPath,
private readonly string $composerLockPath
){}

public function versionsFromLock()
{
$composerLockContents = file_get_contents($this->composerLockPath)
?: throw new \Exception('Couldn\'t open composer lock file from ' . $this->composerLockPath);
$composerJsonContents = file_get_contents($this->composerJsonPath)
?: throw new \Exception('Couldn\'t open composer json file from ' . $this->composerJsonPath);

$types = ['require', 'require-dev'];

foreach ($types as $type) {
foreach ($this->dependencies($composerJsonContents, $type) as $dependency => $version) {
preg_match('#"name": ' . preg_quote($dependency) . ',\s+"version": "(.+)"#m', $composerLockContents, $match);

if (isset($match[1])) {
$lockVersion = str_contains($match[1],'dev')? $match[1] : '^'.$match[1];
$composerJsonContents = str_replace(
$dependency . ': ' . $version,
$dependency . ': "' . $lockVersion . '"',
$composerJsonContents
);
}
}
}

return $composerJsonContents;
}

private
function dependencies(string $composerJsonContents, string $dependencyType): array
{
preg_match('/"' . preg_quote($dependencyType) . '":\s+{([\s\S]+?)}/', $composerJsonContents, $dependencies);
$dependencies = preg_split('/,/ms', $dependencies[1]);
$composerDependencies = [];
foreach ($dependencies as &$dependency) {
$dependency = preg_replace('/\s+/ms', '', $dependency);
$dependency = explode(':', $dependency);
$composerDependencies[$dependency[0]] = $dependency[1];
}

return $composerDependencies;
}
}
89 changes: 33 additions & 56 deletions src/MajorConstraintUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace MartinsR\ComposerConstraintUpdater;

use Composer\Command\BaseCommand;
use Composer\Factory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
Expand All @@ -12,7 +13,6 @@ class MajorConstraintUpdater extends BaseCommand

protected function configure()
{
// @codingStandardsIgnoreStart
$this
->setName('major-update')
->setDescription(
Expand All @@ -22,11 +22,17 @@ protected function configure()
)
->setDefinition([
new InputOption(
'composer-path',
'C',
InputOption::VALUE_REQUIRED,
'composer-json',
null,
InputOption::VALUE_OPTIONAL,
'Composer json file location'
),
new InputOption(
'composer-lock',
null,
InputOption::VALUE_OPTIONAL,
'Composer lock file location'
),
new InputOption(
'constraint',
null,
Expand All @@ -40,26 +46,34 @@ protected function configure()
you can use major-update -C=composer-path --constraint=package/package:^9.0.0. This command will automatically resolve the conflicts in composer.json, saving you the trouble of doing it manually
EOT
);
// @codingStandardsIgnoreEnd
}


protected function execute(InputInterface $input, OutputInterface $output): int
{
$composerPath = $input->getOption('composer-path');
// $composerFileContents = file_get_contents($composerPath);
// $constraints = [];
//
// foreach ($input->getOption('constraint') as $option) {
// $input = explode(':', $option);
// $constraints[$input[0]] = $input[1];
// }
// file_put_contents($composerPath, $this->replaceVersions($composerFileContents, $constraints));
//
// $this->updateComposer();

file_put_contents($composerPath, $this->versionsFromLock($composerPath));
$composerPath = $input->getOption('composer-json') ?? Factory::getComposerFile();
$composerLock = $input->getOption('composer-lock') ?? Factory::getLockFile($composerPath);

$composerFileContents = file_get_contents($composerPath) ?: throw new \Exception('Couldn\'t open the composer json from ' . $composerPath);

$constraints = [];

foreach ($input->getOption('constraint') as $option) {
$input = explode(':', $option);
$constraints[$input[0]] = $input[1];
}

$output->writeln('Building composer.json for update');
file_put_contents($composerPath, $this->replaceVersions($composerFileContents, $constraints));

$output->writeln('Launching composer update');
$this->updateComposer();

$output->writeln('Rebuilding composer.json from lock');
file_put_contents($composerPath, (new ComposerJsonFromLockBuilder($composerPath, $composerLock))->versionsFromLock());

$output->writeln('Composer.json has been successfully updated!');

return 1;
}

Expand All @@ -70,30 +84,6 @@ private function updateComposer(): void
echo $update;
}

public function versionsFromLock(string $composerPath)
{
$composerLockPath = str_replace('composer.json', 'composer.lock', $composerPath);

$composerLockContents = file_get_contents($composerLockPath);
$composerJsonContents = file_get_contents($composerPath);

$types = ['require' => 'packages', 'require-dev' => 'packages-dev'];
foreach ($types as $typeJson => $typeLock) {
preg_match('/"' . preg_quote($typeJson) . '":\s+{([\s\S]+?)}/', $composerJsonContents, $dependencies);
$dependencies = $this->processDependencies(preg_split('/,/ms', $dependencies[1]));
foreach ($dependencies as $dependencyName => $version) {
preg_match('#"name": ' . preg_quote($dependencyName) . ',\s+"version": "(.+)"#m', $composerLockContents, $match);
if (isset($match[1])) {
echo $dependencyName . ': ' . $version;
$composerJsonContents = str_replace($dependencyName . ': ' . $version, $dependencyName . ': "^' . $match[1] . '"', $composerJsonContents);
}
}
}

return $composerJsonContents;

}

public
function replaceVersions(string $composerContents, array $packageConstraints): string
{
Expand All @@ -113,17 +103,4 @@ function replaceVersions(string $composerContents, array $packageConstraints): s

return $composerContents;
}

private
function processDependencies(array $dependencies): array
{
$composerDependencies = [];
foreach ($dependencies as &$dependency) {
$dependency = preg_replace('/\s+/ms', '', $dependency);
$dependency = explode(':', $dependency);
$composerDependencies[$dependency[0]] = $dependency[1];
}

return $composerDependencies;
}
}
}
81 changes: 20 additions & 61 deletions src/MinorConstraintUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace MartinsR\ComposerConstraintUpdater;

use Composer\Command\BaseCommand;
use Composer\Factory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
Expand All @@ -12,30 +13,44 @@ class MinorConstraintUpdater extends BaseCommand

protected function configure()
{
// @codingStandardsIgnoreStart
$this
->setName('minor-update')
->setDescription('Will run composer update and replace versions in your composer.json file to the ones inside lock file')
->setDefinition([
new InputOption('composer-path', 'C', InputOption::VALUE_REQUIRED, 'Composer json file location'),
new InputOption(
'composer-json',
null,
InputOption::VALUE_OPTIONAL,
'Composer json file location'
),
new InputOption(
'composer-lock',
null,
InputOption::VALUE_OPTIONAL,
'Composer lock file location'
),
])
->setHelp(<<<EOT
The <info>minor-update</info> command executes a composer update, which updates your composer.json
file to reflect the actual versions of all packages.
This means it will update the versions of your packages to the latest minor versions, while respecting the version constraints defined in your composer.json file.
EOT
);
// @codingStandardsIgnoreEnd
}


protected function execute(InputInterface $input, OutputInterface $output): int
{
$composerPath = $input->getOption('composer-path');
$composerPath = $input->getOption('composer-json') ?? Factory::getComposerFile();
$composerLock = $input->getOption('composer-lock') ?? Factory::getLockFile($composerPath);

$output->writeln('Launching composer update');
$this->updateComposer();
file_put_contents($composerPath, $this->versionsFromLock($composerPath));

$output->writeln('Rebuilding composer.json from lock file');
file_put_contents($composerPath, (new ComposerJsonFromLockBuilder($composerPath, $composerLock))->versionsFromLock());

$output->writeln('Composer.json has been successfully updated!');
return 1;
}

Expand All @@ -45,60 +60,4 @@ private function updateComposer(): void

echo $update;
}

public function versionsFromLock(string $composerPath)
{
$composerLockPath = str_replace('composer.json', 'composer.lock', $composerPath);

$composerLockContents = file_get_contents($composerLockPath);
$composerJsonContents = file_get_contents($composerPath);

$types = ['require' => 'packages', 'require-dev' => 'packages-dev'];
foreach ($types as $typeJson => $typeLock) {
preg_match('/"' . preg_quote($typeJson) . '":\s+{([\s\S]+?)}/', $composerJsonContents, $dependencies);
$dependencies = $this->processDependencies(preg_split('/,/ms', $dependencies[1]));
foreach ($dependencies as $dependencyName => $version) {
preg_match('#"name": ' . preg_quote($dependencyName) . ',\s+"version": "(.+)"#m', $composerLockContents, $match);
if (isset($match[1])) {
echo $dependencyName . ': ' . $version;
$composerJsonContents = str_replace($dependencyName . ': ' . $version, $dependencyName . ': "^' . $match[1] . '"', $composerJsonContents);
}
}
}

return $composerJsonContents;
}

public
function replaceVersions(string $composerContents, array $packageConstraints): string
{
$requirements = ['require', 'require-dev'];
foreach ($requirements as $requirement) {
preg_match('/"' . preg_quote($requirement) . '"\s*:\s*\{([\s|\S]+?)\}/m', $composerContents, $matches);
$packages = preg_replace('/:\s*"(.*)+?"/m', ': "*"', $matches[1]);
foreach ($packageConstraints as $constraint => $version) {
$packages = preg_replace(
'@"' . preg_quote($constraint) . '"\s*:\s*"\*"@m',
'"' . preg_quote($constraint) . '": "' . $version . '"',
$packages
);
}
$composerContents = preg_replace('/"' . preg_quote($requirement) . '"\s*:\s*\{([\s|\S]+?)\}/m', '"' . $requirement . '": {' . $packages . '}', $composerContents);
}

return $composerContents;
}

private
function processDependencies(array $dependencies): array
{
$composerDependencies = [];
foreach ($dependencies as &$dependency) {
$dependency = preg_replace('/\s+/ms', '', $dependency);
$dependency = explode(':', $dependency);
$composerDependencies[$dependency[0]] = $dependency[1];
}

return $composerDependencies;
}
}
6 changes: 3 additions & 3 deletions tests/unit/ComposerUpdaterTest.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?php

use MartinsR\ComposerConstraintUpdater\ConstraintUpdaterInitCommand;
use MartinsR\ComposerConstraintUpdater\MajorConstraintUpdater;
use PHPUnit\Framework\TestCase;

class ComposerUpdaterTest extends TestCase
{
public function testComposerJsonUpdater(): void{
(new ConstraintUpdaterInitCommand())->replaceVersions(file_get_contents('C:\Users\martins.rucevskis\projects\ComposerConstraintUpdater\tests\resources\composerjson.txt'), ['laravel/framework' => '9.0.0']);
(new MajorConstraintUpdater())->replaceVersions(file_get_contents('C:\Users\martins.rucevskis\projects\ComposerConstraintUpdater\tests\resources\composerjson.txt'), ['laravel/framework' => '9.0.0']);
}

public function testComposerJsonFromLockFile(): void{
(new ConstraintUpdaterInitCommand())->versionsFromLock('C:\Users\martins.rucevskis\projects\ComposerConstraintUpdater\tests\resources\composer.json.txt');
(new MajorConstraintUpdater())->versionsFromLock('C:\Users\martins.rucevskis\projects\ComposerConstraintUpdater\tests\resources\composer.json.txt');
}
}

0 comments on commit e79daaa

Please sign in to comment.