@@ -92,20 +92,26 @@ protected function configure(): void {
9292 $ this ->setName ('install ' );
9393 $ this ->setDescription ('Install Vortex from remote or local repository. ' );
9494 $ this ->setHelp (<<<EOF
95- Interactively install Vortex from the latest stable release into the current directory:
96- php installer --destination=.
97-
98- Non-interactively install Vortex from the latest stable release into the specified directory:
99- php installer --no-interaction --destination=path/to/destination
100-
101- Install Vortex from the stable branch into the specified directory:
102- php installer --uri=https://github.com/drevops/vortex.git@stable --destination=path/to/destination
103-
104- Install Vortex from a specific release into the specified directory:
105- php installer --uri=https://github.com/drevops/vortex.git@1.2.3 --destination=path/to/destination
106-
107- Install Vortex from a specific commit into the specified directory:
108- php installer --uri=https://github.com/drevops/vortex.git@abcd123 --destination=path/to/destination
95+ <info>Interactively install Vortex from the latest stable release into the current directory:</info>
96+ php installer.php --destination=.
97+
98+ <info>Non-interactively install Vortex from the latest stable release into the specified directory:</info>
99+ php installer.php --no-interaction --destination=path/to/destination
100+
101+ <info>Install from the latest auto-discovered stable release (default behavior if --uri is specified):</info>
102+ php installer.php --uri=https://github.com/drevops/vortex.git
103+ php installer.php --uri=https://github.com/drevops/vortex.git#stable
104+
105+ <info>Install using repository URL with specific git ref after #:</info>
106+ php installer.php --uri=https://github.com/drevops/vortex.git#25.11.0
107+ php installer.php --uri=https://github.com/drevops/vortex.git#v1.2.3
108+ php installer.php --uri=https://github.com/drevops/vortex.git#main
109+
110+ <info>Copy GitHub URL directly from your browser:</info>
111+ php installer.php --uri=https://github.com/drevops/vortex/releases/tag/25.11.0
112+ php installer.php --uri=https://github.com/drevops/vortex/tree/1.2.3
113+ php installer.php --uri=https://github.com/drevops/vortex/tree/main
114+ php installer.php --uri=https://github.com/drevops/vortex/commit/abcd123
109115EOF
110116 );
111117 $ this ->addOption (static ::OPTION_DESTINATION , NULL , InputOption::VALUE_REQUIRED , 'Destination directory. Defaults to the current directory. ' );
@@ -138,6 +144,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int
138144
139145 $ this ->header ();
140146
147+ // Only validate if using custom repository or custom reference.
148+ if ($ this ->shouldValidateRepositoryAndRef ()) {
149+ Task::action (
150+ label: 'Validating repository and reference ' ,
151+ action: fn (): string => $ this ->validateRepositoryAndRef (),
152+ hint: fn (): string => sprintf ('Checking repository "%s" and reference "%s" ' , $ this ->config ->get (Config::REPO ), $ this ->config ->get (Config::REF )),
153+ success: fn (string $ return ): string => $ return
154+ );
155+ Tui::line ('' );
156+ }
157+
158+ Tui::line (Tui::dim ('Press any key to continue... ' ));
159+ Tui::getChar ();
160+
141161 $ this ->promptManager ->runPrompts ();
142162
143163 Tui::list ($ this ->promptManager ->getResponsesSummary (), 'Installation summary ' );
@@ -157,7 +177,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
157177 $ this ->config ->set (Config::VERSION , $ version );
158178 return $ version ;
159179 },
160- hint: fn (): string => sprintf ('Downloading from "%s" repository at commit "%s" ' , ...RepositoryDownloader:: parseUri ( $ this ->config ->get (Config::REPO ))),
180+ hint: fn (): string => sprintf ('Downloading from "%s" repository at ref "%s" ' , $ this ->config ->get (Config::REPO ), $ this -> config -> get (Config:: REF )),
161181 success: fn (string $ return ): string => sprintf ('Vortex downloaded (%s) ' , $ return )
162182 );
163183
@@ -301,7 +321,7 @@ protected function resolveOptions(array $arguments, array $options): void {
301321 Env::putFromDotenv ($ dest_env_file );
302322 }
303323
304- [$ repo , $ ref ] = RepositoryDownloader::parseUri ($ options [static ::OPTION_URI ] ?: ' https://github.com/drevops/vortex.git@ stable ' );
324+ [$ repo , $ ref ] = RepositoryDownloader::parseUri ($ options [static ::OPTION_URI ] ?: RepositoryDownloader:: DEFAULT_REPO . ' # stable ' );
305325 $ this ->config ->set (Config::REPO , $ repo );
306326 $ this ->config ->set (Config::REF , $ ref );
307327
@@ -548,9 +568,6 @@ protected function header(): void {
548568 }
549569
550570 Tui::box ($ content , $ title );
551-
552- Tui::line (Tui::dim ('Press any key to continue... ' ));
553- Tui::getChar ();
554571 }
555572
556573 public function footer (): void {
@@ -715,6 +732,73 @@ public function cleanup(): void {
715732 }
716733 }
717734
735+ /**
736+ * Check if repository and reference validation should be performed.
737+ *
738+ * Validation is skipped for default Vortex repo with stable/HEAD refs.
739+ * Validation is required for custom repositories or custom references.
740+ *
741+ * @return bool
742+ * TRUE if validation should be performed, FALSE otherwise.
743+ */
744+ protected function shouldValidateRepositoryAndRef (): bool {
745+ $ repo = $ this ->config ->get (Config::REPO );
746+ $ ref = $ this ->config ->get (Config::REF );
747+
748+ // Check if using default repository and default ref.
749+ $ default_repo_without_git = str_replace ('.git ' , '' , RepositoryDownloader::DEFAULT_REPO );
750+ $ is_default_repo = ($ repo === RepositoryDownloader::DEFAULT_REPO || $ repo === $ default_repo_without_git );
751+ $ is_default_ref = ($ ref === RepositoryDownloader::REF_STABLE || $ ref === RepositoryDownloader::REF_HEAD );
752+
753+ // Skip validation only if both are default.
754+ return !($ is_default_repo && $ is_default_ref );
755+ }
756+
757+ /**
758+ * Validate repository and reference exist before installation.
759+ *
760+ * @return string
761+ * Success message.
762+ *
763+ * @throws \RuntimeException
764+ * If validation fails.
765+ */
766+ protected function validateRepositoryAndRef (): string {
767+ $ repo = $ this ->config ->get (Config::REPO );
768+ $ ref = $ this ->config ->get (Config::REF );
769+
770+ $ downloader = $ this ->getRepositoryDownloader ();
771+
772+ // Determine if this is a remote or local repository.
773+ if (str_starts_with ((string ) $ repo , 'https:// ' ) || str_starts_with ((string ) $ repo , 'git@ ' )) {
774+ // Remote repository.
775+ $ repo_url = str_ends_with ((string ) $ repo , '.git ' ) ? substr ((string ) $ repo , 0 , -4 ) : $ repo ;
776+
777+ // Validate repository exists.
778+ $ downloader ->validateRemoteRepositoryExists ($ repo_url );
779+
780+ // Validate ref exists (skip for special refs).
781+ if ($ ref !== RepositoryDownloader::REF_STABLE && $ ref !== RepositoryDownloader::REF_HEAD ) {
782+ $ downloader ->validateRemoteRefExists ($ repo_url , $ ref );
783+ }
784+
785+ return 'Repository and reference validated successfully ' ;
786+ }
787+ else {
788+ // Local repository.
789+ // Validate repository exists.
790+ $ downloader ->validateLocalRepositoryExists ($ repo );
791+
792+ // Validate ref exists (skip for HEAD).
793+ $ actual_ref = $ ref === RepositoryDownloader::REF_STABLE ? RepositoryDownloader::REF_HEAD : $ ref ;
794+ if ($ actual_ref !== RepositoryDownloader::REF_HEAD ) {
795+ $ downloader ->validateLocalRefExists ($ repo , $ actual_ref );
796+ }
797+
798+ return 'Repository and reference validated successfully ' ;
799+ }
800+ }
801+
718802 /**
719803 * Get the repository downloader.
720804 *
0 commit comments