@@ -161,6 +161,9 @@ protected function downloadFromRemote(string $repo, string $ref, ?string $destin
161161 }
162162 $ repo_url = str_ends_with ($ repo , '.git ' ) ? substr ($ repo , 0 , -4 ) : $ repo ;
163163
164+ // Validate repository exists before proceeding.
165+ $ this ->validateRemoteRepositoryExists ($ repo_url );
166+
164167 $ version = $ ref ;
165168 if ($ ref === RepositoryDownloader::REF_STABLE ) {
166169 $ ref = $ this ->discoverLatestReleaseRemote ($ repo_url );
@@ -174,6 +177,10 @@ protected function downloadFromRemote(string $repo, string $ref, ?string $destin
174177 elseif ($ ref === RepositoryDownloader::REF_HEAD ) {
175178 $ version = 'develop ' ;
176179 }
180+ else {
181+ // Validate ref exists for non-special refs.
182+ $ this ->validateRemoteRefExists ($ repo_url , $ ref );
183+ }
177184
178185 $ url = sprintf ('%s/archive/%s.tar.gz ' , $ repo_url , $ ref );
179186
@@ -190,6 +197,9 @@ protected function downloadFromLocal(string $repo, string $ref, ?string $destina
190197 throw new \InvalidArgumentException ('Destination cannot be null for local downloads. ' );
191198 }
192199
200+ // Validate local repository exists.
201+ $ this ->validateLocalRepositoryExists ($ repo );
202+
193203 $ ref = $ ref === RepositoryDownloader::REF_STABLE ? RepositoryDownloader::REF_HEAD : $ ref ;
194204 $ version = $ ref ;
195205
@@ -200,6 +210,10 @@ protected function downloadFromLocal(string $repo, string $ref, ?string $destina
200210 $ ref = $ this ->git ->getLastShortCommitId ();
201211 $ version = 'develop ' ;
202212 }
213+ else {
214+ // Validate ref exists for non-HEAD refs.
215+ $ this ->validateLocalRefExists ($ repo , $ ref );
216+ }
203217
204218 $ archive_path = $ this ->archiveFromLocal ($ repo , $ ref );
205219 $ this ->archiver ->validate ($ archive_path );
@@ -329,4 +343,116 @@ protected function archiveFromLocal(string $repo, string $ref): string {
329343 return $ temp_file ;
330344 }
331345
346+ /**
347+ * Validate that a remote repository exists and is accessible.
348+ *
349+ * @param string $repo_url
350+ * The repository URL (without .git extension).
351+ *
352+ * @throws \RuntimeException
353+ * If the repository is not accessible.
354+ */
355+ protected function validateRemoteRepositoryExists (string $ repo_url ): void {
356+ $ headers = ['User-Agent ' => 'Vortex-Installer ' ];
357+
358+ $ github_token = Env::get ('GITHUB_TOKEN ' );
359+ if ($ github_token ) {
360+ $ headers ['Authorization ' ] = sprintf ('Bearer %s ' , $ github_token );
361+ }
362+
363+ try {
364+ // Try to access the repository root to verify it exists.
365+ $ response = $ this ->httpClient ->request ('HEAD ' , $ repo_url , ['headers ' => $ headers , 'http_errors ' => FALSE ]);
366+ $ status_code = $ response ->getStatusCode ();
367+
368+ if ($ status_code >= 400 ) {
369+ throw new \RuntimeException (sprintf ('Repository not found or not accessible: "%s" (HTTP %d) ' , $ repo_url , $ status_code ));
370+ }
371+ }
372+ catch (RequestException $ e ) {
373+ throw new \RuntimeException (sprintf ('Unable to access repository: "%s" - %s ' , $ repo_url , $ e ->getMessage ()), $ e ->getCode (), $ e );
374+ }
375+ }
376+
377+ /**
378+ * Validate that a reference exists in a remote repository.
379+ *
380+ * @param string $repo_url
381+ * The repository URL (without .git extension).
382+ * @param string $ref
383+ * The git reference to validate.
384+ *
385+ * @throws \RuntimeException
386+ * If the reference does not exist.
387+ */
388+ protected function validateRemoteRefExists (string $ repo_url , string $ ref ): void {
389+ $ archive_url = sprintf ('%s/archive/%s.tar.gz ' , $ repo_url , $ ref );
390+ $ headers = ['User-Agent ' => 'Vortex-Installer ' ];
391+
392+ $ github_token = Env::get ('GITHUB_TOKEN ' );
393+ if ($ github_token ) {
394+ $ headers ['Authorization ' ] = sprintf ('Bearer %s ' , $ github_token );
395+ }
396+
397+ try {
398+ // Use HEAD request to check if the archive URL exists without downloading.
399+ $ response = $ this ->httpClient ->request ('HEAD ' , $ archive_url , ['headers ' => $ headers , 'http_errors ' => FALSE ]);
400+ $ status_code = $ response ->getStatusCode ();
401+
402+ if ($ status_code === 404 ) {
403+ throw new \RuntimeException (sprintf ('Reference "%s" not found in repository "%s" ' , $ ref , $ repo_url ));
404+ }
405+ elseif ($ status_code >= 400 ) {
406+ throw new \RuntimeException (sprintf ('Unable to verify reference "%s" in repository "%s" (HTTP %d) ' , $ ref , $ repo_url , $ status_code ));
407+ }
408+ }
409+ catch (RequestException $ e ) {
410+ throw new \RuntimeException (sprintf ('Unable to verify reference "%s" in repository "%s" - %s ' , $ ref , $ repo_url , $ e ->getMessage ()), $ e ->getCode (), $ e );
411+ }
412+ }
413+
414+ /**
415+ * Validate that a local repository exists and is a valid git repository.
416+ *
417+ * @param string $repo
418+ * The local repository path.
419+ *
420+ * @throws \RuntimeException
421+ * If the repository does not exist or is not a valid git repository.
422+ */
423+ protected function validateLocalRepositoryExists (string $ repo ): void {
424+ if (!is_dir ($ repo )) {
425+ throw new \RuntimeException (sprintf ('Local repository path does not exist: "%s" ' , $ repo ));
426+ }
427+
428+ if (!is_dir ($ repo . '/.git ' )) {
429+ throw new \RuntimeException (sprintf ('Path is not a git repository: "%s" ' , $ repo ));
430+ }
431+ }
432+
433+ /**
434+ * Validate that a reference exists in a local repository.
435+ *
436+ * @param string $repo
437+ * The local repository path.
438+ * @param string $ref
439+ * The git reference to validate.
440+ *
441+ * @throws \RuntimeException
442+ * If the reference does not exist.
443+ */
444+ protected function validateLocalRefExists (string $ repo , string $ ref ): void {
445+ if (!$ this ->git instanceof Git) {
446+ $ this ->git = new Git ($ repo );
447+ }
448+
449+ try {
450+ // Use git rev-parse to check if the ref exists.
451+ $ this ->git ->run ('rev-parse ' , '--verify ' , $ ref );
452+ }
453+ catch (\Exception $ e ) {
454+ throw new \RuntimeException (sprintf ('Reference "%s" not found in local repository "%s" ' , $ ref , $ repo ), $ e ->getCode (), $ e );
455+ }
456+ }
457+
332458}
0 commit comments