request($baseUrl, $token) ->get('/user') ->throw() ->json(); return new GitAuthenticatedUser( providerUserId: (string) $payload['id'], login: $payload['login'], name: blank($payload['full_name'] ?? null) ? null : $payload['full_name'], ); } public function getRepository( string $baseUrl, string $token, string $owner, string $repository, ): GitRepository { /** @var array{ * default_branch?: string|null, * id: int|string, * name: string, * full_name: string, * html_url: string, * owner: array{login: string}, * private: bool * } $payload */ $payload = $this->request($baseUrl, $token) ->get('/repos/'.rawurlencode($owner).'/'.rawurlencode($repository)) ->throw() ->json(); return $this->mapRepository($payload); } /** * @return array */ public function listRepositories(string $baseUrl, string $token): array { $page = 1; $repositories = []; do { /** @var array $payload */ $payload = $this->request($baseUrl, $token) ->get('/user/repos', [ 'limit' => 100, 'page' => $page, ]) ->throw() ->json(); foreach ($payload as $repository) { $repositories[] = $this->mapRepository($repository); } $page++; } while (count($payload) === 100); return $repositories; } /** * @return array */ public function listBranches( string $baseUrl, string $token, string $owner, string $repository, ): array { $page = 1; $branches = []; do { /** @var array $payload */ $payload = $this->request($baseUrl, $token) ->get( '/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/branches', [ 'limit' => 100, 'page' => $page, ], ) ->throw() ->json(); foreach ($payload as $branch) { $branches[] = new GitBranch( name: $branch['name'], ); } $page++; } while (count($payload) === 100); return $branches; } /** * @return array */ public function listPullRequests( string $baseUrl, string $token, string $owner, string $repository, ): array { $page = 1; $pullRequests = []; do { /** @var array $payload */ $payload = $this->request($baseUrl, $token) ->get( '/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/pulls', [ 'limit' => 100, 'page' => $page, 'state' => 'all', ], ) ->throw() ->json(); foreach ($payload as $pullRequest) { $status = ($pullRequest['merged'] ?? false) ? 'merged' : $pullRequest['state']; $pullRequests[] = new GitPullRequest( number: (string) $pullRequest['number'], title: $pullRequest['title'], status: $status, sourceBranch: $pullRequest['head']['ref'], htmlUrl: $pullRequest['html_url'], ); } $page++; } while (count($payload) === 100); return $pullRequests; } public function createBranch( string $baseUrl, string $token, string $owner, string $repository, string $newBranchName, ?string $oldRefName = null, ): GitBranch { $payload = [ 'new_branch_name' => $newBranchName, ]; if ($oldRefName !== null && $oldRefName !== '') { $payload['old_ref_name'] = $oldRefName; } /** @var array{name: string} $response */ $response = $this->request($baseUrl, $token) ->post( '/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/branches', $payload, ) ->throw() ->json(); return new GitBranch( name: $response['name'], ); } private function request(string $baseUrl, string $token): PendingRequest { return Http::acceptJson() ->baseUrl($this->apiBaseUrl($baseUrl)) ->connectTimeout(5) ->timeout(10) ->withHeader('Authorization', 'token '.$token); } private function apiBaseUrl(string $baseUrl): string { $normalizedBaseUrl = Str::of(trim($baseUrl)) ->rtrim('/') ->value(); if (Str::endsWith(Str::lower($normalizedBaseUrl), '/api/v1')) { return $normalizedBaseUrl; } return Str::of($normalizedBaseUrl) ->append('/api/v1') ->value(); } /** * @param array{ * default_branch?: string|null, * id: int|string, * name: string, * full_name: string, * html_url: string, * owner: array{login: string}, * private: bool * } $repository */ private function mapRepository(array $repository): GitRepository { return new GitRepository( providerRepositoryId: (string) $repository['id'], owner: $repository['owner']['login'], name: $repository['name'], fullName: $repository['full_name'], htmlUrl: $repository['html_url'], defaultBranch: blank($repository['default_branch'] ?? null) ? null : $repository['default_branch'], isPrivate: $repository['private'], ); } }