apiBase, rawurlencode($owner), rawurlencode($repo), rawurlencode($state), $page, self::PER_PAGE ); $pageIssues = $this->fetch($url); if ($pageIssues === []) { break; } foreach ($pageIssues as $issue) { $issues[] = [ 'number' => (int) $issue['number'], 'title' => (string) $issue['title'], 'body' => isset($issue['body']) ? (string) $issue['body'] : null, 'author' => self::authorLogin($issue), 'labels' => array_map( static fn (array $label): array => [ 'name' => (string) $label['name'], 'color' => (string) $label['color'], ], $issue['labels'] ?? [] ), ]; } if (count($pageIssues) < self::PER_PAGE) { break; } } return $issues; } public function listOpenPullRequests(string $owner, string $repo): array { $pulls = []; for ($page = 1; $page <= self::MAX_PAGES; $page++) { $url = sprintf( '%s/repos/%s/%s/issues?state=open&type=pulls&page=%d&limit=%d', $this->apiBase, rawurlencode($owner), rawurlencode($repo), $page, self::PER_PAGE ); $pagePulls = $this->fetch($url); foreach ($pagePulls as $pull) { $pulls[] = [ 'number' => (int) $pull['number'], 'title' => (string) ($pull['title'] ?? ''), 'body' => isset($pull['body']) ? (string) $pull['body'] : null, 'author' => self::authorLogin($pull), ]; } if (count($pagePulls) < self::PER_PAGE) { break; } } return $pulls; } /** * Issue #113: Login des Kontos, das Issue/PR angelegt hat (Feld "user" der Codeberg-API). * * @param array $item */ private static function authorLogin(array $item): ?string { $login = is_array($item['user'] ?? null) ? ($item['user']['login'] ?? null) : null; return is_string($login) && $login !== '' ? $login : null; } public function repoPermissions(string $owner, string $repo): array { $url = sprintf('%s/repos/%s/%s', $this->apiBase, rawurlencode($owner), rawurlencode($repo)); $data = $this->fetch($url); $permissions = is_array($data['permissions'] ?? null) ? $data['permissions'] : []; return ['push' => (bool) ($permissions['push'] ?? false)]; } public function listWebhooks(string $owner, string $repo): array { $url = sprintf('%s/repos/%s/%s/hooks', $this->apiBase, rawurlencode($owner), rawurlencode($repo)); $data = $this->fetch($url); return array_map( static fn (array $hook): array => [ 'active' => (bool) ($hook['active'] ?? false), 'url' => (string) ($hook['config']['url'] ?? ''), 'content_type' => (string) ($hook['config']['content_type'] ?? ''), ], $data ); } /** @return array */ private function fetch(string $url): array { $headers = ['Accept: application/json']; if ($this->token !== null && $this->token !== '') { $headers[] = 'Authorization: token ' . $this->token; } $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => 15, ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($body === false) { throw new RuntimeException('Codeberg-API nicht erreichbar: ' . $error); } if ($status >= 400) { throw new CodebergApiException($status, 'Codeberg-API-Fehler (HTTP ' . $status . '): ' . $body); } $decoded = json_decode((string) $body, true); return is_array($decoded) ? $decoded : []; } }