Skip to content
Open
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
40 changes: 29 additions & 11 deletions src/VCS/Adapter/Git/GitHub.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,22 +106,40 @@ public function createRepository(string $owner, string $repositoryName, bool $pr
*/
public function searchRepositories(string $owner, int $page, int $per_page, string $search = ''): array
{
$url = '/search/repositories';
$repositories = [];

$response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"], [
'q' => "{$search} user:{$owner} fork:true",
'page' => $page,
'per_page' => $per_page,
'sort' => 'updated'
]);
$currentPage = 1;
while (true) {
$url = '/installation/repositories';
$response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"], [
'page' => $currentPage,
'per_page' => 100, // Maximum allowed by GitHub API
]);

if (!isset($response['body']['repositories'])) {
throw new Exception("Repositories list missing in the response.");
}

// Filter repositories to only include those that match the search query.
$filteredRepositories = array_filter($response['body']['repositories'], fn ($repo) => empty($search) || stripos($repo['name'], $search) !== false);

// Merge with result so far.
$repositories = array_merge($repositories, $filteredRepositories);

if (!isset($response['body']['items'])) {
throw new Exception("Repositories list missing in the response.");
// If less than 100 repositories are returned, we have fetched all repositories.
if (\count($response['body']['repositories']) < 100) {
break;
}

// Increment page number to fetch next page.
$currentPage++;
}
Comment on lines +109 to 136
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Performance regression: fetches all pages even without a search term.

When $search is empty, this implementation still fetches every page from the API before slicing. For installations with hundreds or thousands of repositories, this creates unnecessary API calls and memory usage.

When no search filtering is needed, delegate pagination directly to the API:

Proposed fix
 public function searchRepositories(string $owner, int $page, int $per_page, string $search = ''): array
 {
+    $url = '/installation/repositories';
+
+    // When no search term, delegate pagination directly to the API
+    if (empty($search)) {
+        $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"], [
+            'page' => $page,
+            'per_page' => $per_page,
+        ]);
+
+        if (!isset($response['body']['repositories'])) {
+            throw new Exception("Repositories list missing in the response.");
+        }
+
+        return [
+            'items' => $response['body']['repositories'],
+            'total' => $response['body']['total_count'] ?? \count($response['body']['repositories']),
+        ];
+    }
+
+    // When searching, fetch all pages and filter locally
     $repositories = [];
-
     $currentPage = 1;
     while (true) {
-        $url = '/installation/repositories';
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"], [
             'page' => $currentPage,
             'per_page' => 100, // Maximum allowed by GitHub API
         ]);

         if (!isset($response['body']['repositories'])) {
             throw new Exception("Repositories list missing in the response.");
         }

-        // Filter repositories to only include those that match the search query.
-        $filteredRepositories = array_filter($response['body']['repositories'], fn ($repo) => empty($search) || stripos($repo['name'], $search) !== false);
+        $filteredRepositories = array_filter($response['body']['repositories'], fn ($repo) => stripos($repo['name'], $search) !== false);

-        // Merge with result so far.
         $repositories = array_merge($repositories, $filteredRepositories);

-        // If less than 100 repositories are returned, we have fetched all repositories.
         if (\count($response['body']['repositories']) < 100) {
             break;
         }

-        // Increment page number to fetch next page.
         $currentPage++;
     }
🤖 Prompt for AI Agents
In `@src/VCS/Adapter/Git/GitHub.php` around lines 109 - 136, The loop always pages
through all repositories even when $search is empty; change the method in GitHub
(the block using $repositories, $currentPage and the call(self::METHOD_GET,
'/installation/repositories', ...)) to short-circuit: if empty($search) make a
single API call with the requested 'page' and 'per_page' parameters and return
or use that result directly (no while loop), otherwise keep the existing
paging+filtering logic (array_filter on $response['body']['repositories']) to
aggregate pages until fewer than 100 items are returned; ensure you still throw
the same exception when 'repositories' is missing.


$repositoriesInRequestedPage = \array_slice($repositories, ($page - 1) * $per_page, $per_page);

return [
'items' => $response['body']['items'],
'total' => $response['body']['total_count'],
'items' => $repositoriesInRequestedPage,
'total' => \count($repositories),
];
}

Expand Down