44
55namespace Rector \Jack \Command ;
66
7- use Composer \Semver \Comparator ;
8- use Composer \Semver \VersionParser ;
97use Nette \Utils \FileSystem ;
108use Nette \Utils \Json ;
11- use Rector \Jack \FileSystem \ComposerJsonPackageVersionUpdater ;
12- use Rector \Jack \Utils \JsonFileLoader ;
9+ use Rector \Jack \ComposerProcessor \RaiseToInstalledComposerProcessor ;
1310use Symfony \Component \Console \Command \Command ;
1411use Symfony \Component \Console \Input \InputInterface ;
12+ use Symfony \Component \Console \Input \InputOption ;
1513use Symfony \Component \Console \Output \OutputInterface ;
1614use Symfony \Component \Console \Style \SymfonyStyle ;
1715use Webmozart \Assert \Assert ;
1816
1917final class RaiseToInstalledCommand extends Command
2018{
2119 public function __construct (
22- private readonly VersionParser $ versionParser
20+ private readonly RaiseToInstalledComposerProcessor $ raiseToInstalledComposerProcessor ,
2321 ) {
2422 parent ::__construct ();
2523 }
@@ -32,133 +30,60 @@ protected function configure(): void
3230 'Raise your version in "composer.json" to installed one to get the latest version available in any composer update '
3331 );
3432
35- // @todo add dry-run mode
33+ $ this ->addOption (
34+ 'dry-run ' ,
35+ null ,
36+ InputOption::VALUE_NONE ,
37+ 'Only show diff of "composer.json" changes, do not write the file '
38+ );
3639 }
3740
3841 protected function execute (InputInterface $ input , OutputInterface $ output ): int
3942 {
4043 $ symfonyStyle = new SymfonyStyle ($ input , $ output );
44+ $ isDryRun = (bool ) $ input ->getOption ('dry-run ' );
4145
4246 $ symfonyStyle ->writeln ('<fg=green>Analyzing "/vendor/composer/installed.json" for versions</> ' );
4347
44- $ installedPackagesToVersions = $ this ->resolveInstalledPackagesToVersions ();
45-
4648 // load composer.json and replace versions in "require" and "require-dev",
4749 $ composerJsonFilePath = getcwd () . '/composer.json ' ;
4850
4951 Assert::fileExists ($ composerJsonFilePath );
5052 $ composerJsonContents = FileSystem::read ($ composerJsonFilePath );
51- $ composerJson = Json::decode ($ composerJsonContents , true );
52-
53- $ hasChanged = false ;
54-
55- // iterate require and require-dev sections and check if installed version is newer one than in composer.json
56- // if so, replace it
57- foreach ($ composerJson ['require ' ] ?? [] as $ packageName => $ packageVersion ) {
58- if (! isset ($ installedPackagesToVersions [$ packageName ])) {
59- continue ;
60- }
61-
62- $ installedVersion = $ installedPackagesToVersions [$ packageName ];
63-
64- // special case for unions
65- if (str_contains ((string ) $ packageVersion , '| ' )) {
66- $ passingVersionKeys = [];
67-
68- $ unionPackageVersions = explode ('| ' , (string ) $ packageVersion );
69- foreach ($ unionPackageVersions as $ key => $ unionPackageVersion ) {
70- $ unionPackageConstraint = $ this ->versionParser ->parseConstraints ($ unionPackageVersion );
71-
72- if (Comparator::greaterThanOrEqualTo (
73- $ installedVersion ,
74- $ unionPackageConstraint ->getLowerBound ()
75- ->getVersion ()
76- )) {
77- $ passingVersionKeys [] = $ key ;
78- }
79- }
80-
81- // nothing we can do, as lower union version is passing
82- if ($ passingVersionKeys === [0 ]) {
83- continue ;
84- }
85-
86- // higher version is meet, let's drop the lower one
87- if ($ passingVersionKeys === [0 , 1 ]) {
88- $ newPackageVersion = $ unionPackageVersions [1 ];
89-
90- $ composerJsonContents = ComposerJsonPackageVersionUpdater::update (
91- $ composerJsonContents ,
92- $ packageName ,
93- $ newPackageVersion
94- );
95-
96- $ hasChanged = true ;
97- continue ;
98- }
99- }
100-
101- $ normalizedInstalledVersion = $ this ->versionParser ->normalize ($ installedVersion );
102- $ installedPackageConstraint = $ this ->versionParser ->parseConstraints ($ packageVersion );
103-
104- $ normalizedConstraintVersion = $ this ->versionParser ->normalize (
105- $ installedPackageConstraint ->getLowerBound ()
106- ->getVersion ()
107- );
108-
109- // remove "-dev" suffix
110- $ normalizedConstraintVersion = str_replace ('-dev ' , '' , $ normalizedConstraintVersion );
111-
112- // all equal
113- if ($ normalizedConstraintVersion === $ normalizedInstalledVersion ) {
114- continue ;
115- }
116-
117- [$ major , $ minor , $ patch ] = explode ('. ' , $ normalizedInstalledVersion );
118-
119- $ newRequiredVersion = sprintf ('^%s.%s ' , $ major , $ minor );
120-
121- // lets update
122- $ composerJsonContents = ComposerJsonPackageVersionUpdater::update (
123- $ composerJsonContents ,
124- $ packageName ,
125- $ newRequiredVersion
126- );
127-
128- $ hasChanged = true ;
129- continue ;
130- // focus on minor only
131- // or on patch in case of 0.*
132- }
13353
134- if ( $ hasChanged ) {
135- $ symfonyStyle -> success ( ' Updating "composer.json" with installed versions ' );
136- FileSystem:: write ( $ composerJsonFilePath , $ composerJsonContents , null );
137- } else {
54+ $ raiseToInstalledResult = $ this -> raiseToInstalledComposerProcessor -> process ( $ composerJsonContents );
55+
56+ $ changedPackages = $ raiseToInstalledResult -> getChangedPackageVersions ( );
57+ if ( $ changedPackages === []) {
13858 $ symfonyStyle ->success ('No changes made to "composer.json" ' );
59+ return self ::SUCCESS ;
13960 }
14061
141- return self ::SUCCESS ;
142- }
143-
144- /**
145- * @return array<string, string>
146- */
147- private function resolveInstalledPackagesToVersions (): array
148- {
149- $ installedJsonFilePath = getcwd () . '/vendor/composer/installed.json ' ;
150-
151- $ installedJson = JsonFileLoader::loadFileToJson ($ installedJsonFilePath );
152- Assert::keyExists ($ installedJson , 'packages ' );
153-
154- $ installedPackagesToVersions = [];
155- foreach ($ installedJson ['packages ' ] as $ installedPackage ) {
156- $ packageName = $ installedPackage ['name ' ];
157- $ packageVersion = $ installedPackage ['version ' ];
62+ if ($ isDryRun === false ) {
63+ $ changedComposerJsonContents = $ raiseToInstalledResult ->getComposerJsonContents ();
64+ FileSystem::write ($ composerJsonFilePath , $ changedComposerJsonContents . PHP_EOL , null );
65+ }
15866
159- $ installedPackagesToVersions [$ packageName ] = $ packageVersion ;
67+ $ symfonyStyle ->success (sprintf (
68+ '%d package%s %s changed to installed versions.%s%s "composer update --lock" to update "composer.lock" hash ' ,
69+ count ($ changedPackages ),
70+ count ($ changedPackages ) === 1 ? '' : 's ' ,
71+ $ isDryRun ? 'would be (is "--dry-run") ' : 'were updated ' ,
72+ PHP_EOL ,
73+ $ isDryRun ? 'Then you would run ' : 'Now run ' ,
74+ ));
75+
76+ foreach ($ changedPackages as $ changedPackage ) {
77+ $ symfonyStyle ->writeln (sprintf (
78+ ' * <fg=green>%s</> (<fg=yellow>%s</> => <fg=yellow>%s</>) ' ,
79+ $ changedPackage ->getPackageName (),
80+ $ changedPackage ->getOldVersion (),
81+ $ changedPackage ->getNewVersion ()
82+ ));
16083 }
16184
162- return $ installedPackagesToVersions ;
85+ $ symfonyStyle ->newLine ();
86+
87+ return self ::SUCCESS ;
16388 }
16489}
0 commit comments