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
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"require": {
"php": ">=8.2",
"composer/semver": "^3.4",
"illuminate/container": "^12.0",
"illuminate/container": "^12.14",
"nette/utils": "^4.0",
"symfony/console": "^6.4",
"symfony/finder": "^7.2",
Expand Down Expand Up @@ -67,3 +67,4 @@




61 changes: 21 additions & 40 deletions src/Command/OpenVersionsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@

use Nette\Utils\FileSystem;
use Nette\Utils\Json;
use Nette\Utils\Strings;
use Rector\Jack\Composer\ComposerOutdatedResponseProvider;
use Rector\Jack\Composer\NextVersionResolver;
use Rector\Jack\ComposerProcessor\OpenVersionsComposerProcessor;
use Rector\Jack\Enum\ComposerKey;
use Rector\Jack\OutdatedComposerFactory;
use Symfony\Component\Console\Command\Command;
Expand All @@ -20,9 +19,9 @@
final class OpenVersionsCommand extends Command
{
public function __construct(
private readonly NextVersionResolver $nextVersionResolver,
private readonly OutdatedComposerFactory $outdatedComposerFactory,
private readonly ComposerOutdatedResponseProvider $composerOutdatedResponseProvider,
private readonly OpenVersionsComposerProcessor $openVersionsComposerProcessor,
) {
parent::__construct();
}
Expand Down Expand Up @@ -93,58 +92,40 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$composerJsonContents = FileSystem::read($composerJsonFilePath);

$outdatedPackages = $outdatedComposer->getPackagesShuffled($onlyDev, $packagePrefix);

$openedPackageCount = 0;
foreach ($outdatedPackages as $outdatedPackage) {
$composerVersion = $outdatedPackage->getComposerVersion();

// already filled with open version
if (str_contains($composerVersion, '|')) {
continue;
}

// convert composer version to next version
$nextVersion = $this->nextVersionResolver->resolve($outdatedPackage->getName(), $composerVersion);
$openedVersion = $composerVersion . '|' . $nextVersion;

// replace using regex, to keep original composer.json format
$composerJsonContents = Strings::replace(
$composerJsonContents,
// find
sprintf('#"%s": "(.*?)"#', $outdatedPackage->getName()),
// replace
sprintf('"%s": "%s"', $outdatedPackage->getName(), $openedVersion)
);

$symfonyStyle->writeln(sprintf(
' * Opened "<fg=green>%s</>" package to "<fg=yellow>%s</>" version',
$outdatedPackage->getName(),
$openedVersion
));
$changedPackageVersionsResult = $this->openVersionsComposerProcessor->process(
$composerJsonContents,
$outdatedComposer,
$limit,
$onlyDev,
$packagePrefix
);

++$openedPackageCount;
if ($openedPackageCount >= $limit) {
// we've reached the limit, so we can stop
break;
}
}
$openedPackages = $changedPackageVersionsResult->getChangedPackageVersions();
$changedComposerJson = $changedPackageVersionsResult->getComposerJsonContents();

if ($isDryRun === false) {
// update composer.json file, only if no --dry-run
FileSystem::write($composerJsonFilePath, $composerJsonContents . PHP_EOL);
FileSystem::write($composerJsonFilePath, $changedComposerJson . PHP_EOL, null);
}

$symfonyStyle->success(
sprintf(
'%d packages %s opened up to the next nearest version.%s%s "composer update" to push versions up',
$openedPackageCount,
count($openedPackages),
$isDryRun ? 'would be (is "--dry-run")' : 'were',
PHP_EOL,
$isDryRun ? 'Then you would run' : 'Now run'
)
);

foreach ($openedPackages as $openedPackage) {
$symfonyStyle->writeln(sprintf(
' * Opened "<fg=green>%s</>" package to "<fg=yellow>%s</>" version',
$openedPackage->getPackageName(),
$openedPackage->getNewVersion()
));
}

return self::SUCCESS;
}
}
89 changes: 89 additions & 0 deletions src/Command/RaiseToInstalledCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace Rector\Jack\Command;

use Nette\Utils\FileSystem;
use Nette\Utils\Json;
use Rector\Jack\ComposerProcessor\RaiseToInstalledComposerProcessor;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Webmozart\Assert\Assert;

final class RaiseToInstalledCommand extends Command
{
public function __construct(
private readonly RaiseToInstalledComposerProcessor $raiseToInstalledComposerProcessor,
) {
parent::__construct();
}

protected function configure(): void
{
$this->setName('raise-to-lock');

$this->setDescription(
'Raise your version in "composer.json" to installed one to get the latest version available in any composer update'
);

$this->addOption(
'dry-run',
null,
InputOption::VALUE_NONE,
'Only show diff of "composer.json" changes, do not write the file'
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$symfonyStyle = new SymfonyStyle($input, $output);
$isDryRun = (bool) $input->getOption('dry-run');

$symfonyStyle->writeln('<fg=green>Analyzing "/vendor/composer/installed.json" for versions</>');

// load composer.json and replace versions in "require" and "require-dev",
$composerJsonFilePath = getcwd() . '/composer.json';

Assert::fileExists($composerJsonFilePath);
$composerJsonContents = FileSystem::read($composerJsonFilePath);

$raiseToInstalledResult = $this->raiseToInstalledComposerProcessor->process($composerJsonContents);

$changedPackages = $raiseToInstalledResult->getChangedPackageVersions();
if ($changedPackages === []) {
$symfonyStyle->success('No changes made to "composer.json"');
return self::SUCCESS;
}

if ($isDryRun === false) {
$changedComposerJsonContents = $raiseToInstalledResult->getComposerJsonContents();
FileSystem::write($composerJsonFilePath, $changedComposerJsonContents . PHP_EOL, null);
}

$symfonyStyle->success(sprintf(
'%d package%s %s changed to installed versions.%s%s "composer update --lock" to update "composer.lock" hash',
count($changedPackages),
count($changedPackages) === 1 ? '' : 's',
$isDryRun ? 'would be (is "--dry-run")' : 'were updated',
PHP_EOL,
$isDryRun ? 'Then you would run' : 'Now run',
));

foreach ($changedPackages as $changedPackage) {
$symfonyStyle->writeln(sprintf(
' * <fg=green>%s</> (<fg=yellow>%s</> => <fg=yellow>%s</>)',
$changedPackage->getPackageName(),
$changedPackage->getOldVersion(),
$changedPackage->getNewVersion()
));
}

$symfonyStyle->newLine();

return self::SUCCESS;
}
}
32 changes: 32 additions & 0 deletions src/Composer/InstalledVersionResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Rector\Jack\Composer;

use Rector\Jack\Utils\JsonFileLoader;
use Webmozart\Assert\Assert;

final class InstalledVersionResolver
{
/**
* @return array<string, string>
*/
public function resolve(): array
{
$installedJsonFilePath = getcwd() . '/vendor/composer/installed.json';

$installedJson = JsonFileLoader::loadFileToJson($installedJsonFilePath);
Assert::keyExists($installedJson, 'packages');

$installedPackagesToVersions = [];
foreach ($installedJson['packages'] as $installedPackage) {
$packageName = $installedPackage['name'];
$packageVersion = $installedPackage['version'];

$installedPackagesToVersions[$packageName] = $packageVersion;
}

return $installedPackagesToVersions;
}
}
15 changes: 15 additions & 0 deletions src/Composer/VersionComparator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Rector\Jack\Composer;

final class VersionComparator
{
public static function areAndMinorVersionsEqual(string $firstVersion, string $secondVersion): bool
{
[$firstMajor, $firstMinor] = explode('.', $firstVersion);
[$secondMajor, $secondMinor] = explode('.', $secondVersion);

// if major and minor are equal, we can skip the update
return $firstMajor === $secondMajor && $firstMinor === $secondMinor;
}
}
67 changes: 67 additions & 0 deletions src/ComposerProcessor/OpenVersionsComposerProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace Rector\Jack\ComposerProcessor;

use Rector\Jack\Composer\NextVersionResolver;
use Rector\Jack\FileSystem\ComposerJsonPackageVersionUpdater;
use Rector\Jack\ValueObject\ChangedPackageVersion;
use Rector\Jack\ValueObject\ComposerProcessorResult\ChangedPackageVersionsResult;
use Rector\Jack\ValueObject\OutdatedComposer;

/**
* @see \Rector\Jack\Tests\ComposerProcessor\OpenVersionsComposerProcessor\OpenVersionsComposerProcessorTest
*/
final class OpenVersionsComposerProcessor
{
public function __construct(
private NextVersionResolver $nextVersionResolver
) {
}

public function process(
string $composerJsonContents,
OutdatedComposer $outdatedComposer,
int $limit,
bool $onlyDev,
?string $packagePrefix
): ChangedPackageVersionsResult {
$outdatedPackages = $outdatedComposer->getPackagesShuffled($onlyDev, $packagePrefix);

$openedPackages = [];

foreach ($outdatedPackages as $outdatedPackage) {
$composerVersion = $outdatedPackage->getComposerVersion();

// already filled with open version
if (str_contains($composerVersion, '|')) {
continue;
}

// convert composer version to next version
$nextVersion = $this->nextVersionResolver->resolve($outdatedPackage->getName(), $composerVersion);
$openedVersion = $composerVersion . '|' . $nextVersion;

// replace using regex, to keep original composer.json format
$composerJsonContents = ComposerJsonPackageVersionUpdater::update(
$composerJsonContents,
$outdatedPackage->getName(),
$openedVersion
);

$openedPackages[] = new ChangedPackageVersion(
$outdatedPackage->getName(),
$composerVersion,
$openedVersion,
);

if (count($openedPackages) >= $limit) {
// we've reached the limit, so we can stop
break;
}
}

return new ChangedPackageVersionsResult($composerJsonContents, $openedPackages);
}
}
Loading