@@ -51,6 +51,8 @@ class InstallCommand extends GpmCommand
5151 protected $ demo_processing = [];
5252 /** @var string */
5353 protected $ all_yes ;
54+ /** @var array */
55+ protected $ requested_versions = [];
5456
5557 /**
5658 * @return void
@@ -81,7 +83,7 @@ protected function configure(): void
8183 ->addArgument (
8284 'package ' ,
8385 InputArgument::IS_ARRAY | InputArgument::REQUIRED ,
84- 'Package(s) to install. Use "bin/gpm index" to list packages. Use "bin/gpm direct-install" to install a specific version '
86+ 'Package(s) to install. Use "bin/gpm index" to list packages. Append :version to install a specific version (e.g., typhoon:2.4.8) '
8587 )
8688 ->setDescription ('Performs the installation of plugins and themes ' )
8789 ->setHelp ('The <info>install</info> command allows to install plugins and themes ' );
@@ -121,6 +123,18 @@ protected function serve(): int
121123 $ this ->destination = realpath ($ input ->getOption ('destination ' ));
122124
123125 $ packages = array_map ('strtolower ' , $ input ->getArgument ('package ' ));
126+
127+ // Parse package:version syntax (e.g., typhoon:2.4.8)
128+ $ this ->requested_versions = [];
129+ foreach ($ packages as &$ package ) {
130+ if (str_contains ($ package , ': ' )) {
131+ [$ slug , $ version ] = explode (': ' , $ package , 2 );
132+ $ package = $ slug ;
133+ $ this ->requested_versions [$ slug ] = ltrim ($ version , 'v ' );
134+ }
135+ }
136+ unset($ package );
137+
124138 $ this ->data = $ this ->gpm ->findPackages ($ packages );
125139 $ this ->loadLocalConfig ();
126140
@@ -528,6 +542,43 @@ private function processGpm(Package $package, bool $is_update = false)
528542 {
529543 $ io = $ this ->getIO ();
530544
545+ // Handle specific version request (e.g., typhoon:2.4.8)
546+ $ requestedVersion = $ this ->requested_versions [$ package ->slug ] ?? null ;
547+ if ($ requestedVersion ) {
548+ $ changelog = $ package ->getChangelog ();
549+ $ versionValid = version_compare ($ package ->version , $ requestedVersion , '== ' );
550+
551+ if (!$ versionValid && $ changelog ) {
552+ foreach ($ changelog as $ changelogVersion => $ entry ) {
553+ preg_match ("/[\w\-.]+/ " , (string ) $ changelogVersion , $ cleanVersion );
554+ if ($ cleanVersion && version_compare ($ cleanVersion [0 ], $ requestedVersion , '== ' )) {
555+ $ versionValid = true ;
556+ break ;
557+ }
558+ }
559+ }
560+
561+ if (!$ versionValid ) {
562+ $ io ->writeln ("<red>Version <white> {$ requestedVersion }</white> not found for </red><cyan> {$ package ->name }</cyan> " );
563+ if ($ changelog ) {
564+ $ versions = [];
565+ foreach ($ changelog as $ v => $ entry ) {
566+ preg_match ("/[\w\-.]+/ " , (string ) $ v , $ clean );
567+ if ($ clean ) {
568+ $ versions [] = $ clean [0 ];
569+ }
570+ }
571+ $ io ->writeln ('Known versions: <yellow> ' . implode ('</yellow>, <yellow> ' , $ versions ) . '</yellow> ' );
572+ }
573+ $ io ->newLine ();
574+ return false ;
575+ }
576+
577+ // Override version and download URL for the specific version
578+ $ package ->zipball_url = "https://getgrav.org/download/ {$ package ->package_type }/ {$ package ->slug }/ {$ requestedVersion }" ;
579+ $ package ->version = $ requestedVersion ;
580+ }
581+
531582 $ version = $ package ->available ?? $ package ->version ;
532583 $ license = Licenses::get ($ package ->slug );
533584
0 commit comments