*/ public function listTags(string $registryImage): array { $parts = explode('/', $registryImage); if (\count($parts) < 3) { throw new \InvalidArgumentException(sprintf('Invalid registry image format: "%s". Expected "registry/owner/package".', $registryImage)); } $owner = $parts[1]; $package = implode('/', \array_slice($parts, 2)); $bearerToken = $this->getBearerToken($owner, $package); $url = sprintf('%s/v2/%s/%s/tags/list', $this->giteaApiUrl, $owner, $package); $response = $this->httpClient->request('GET', $url, [ 'headers' => [ 'Authorization' => sprintf('Bearer %s', $bearerToken), ], 'timeout' => 10, ]); $data = $response->toArray(); $tags = $data['tags'] ?? []; usort($tags, function (string $a, string $b): int { $aIsVersion = str_starts_with($a, 'v'); $bIsVersion = str_starts_with($b, 'v'); if ($aIsVersion && $bIsVersion) { return version_compare(ltrim($b, 'v'), ltrim($a, 'v')); } if ($aIsVersion) { return -1; } if ($bIsVersion) { return 1; } return strcmp($a, $b); }); return $tags; } private function getBearerToken(string $owner, string $package): string { $tokenUrl = sprintf( '%s/v2/token?service=container_registry&scope=repository:%s/%s:pull', $this->giteaApiUrl, $owner, $package, ); $response = $this->httpClient->request('GET', $tokenUrl, [ 'auth_basic' => [$this->giteaApiToken, ''], 'timeout' => 10, ]); $data = $response->toArray(); return $data['token'] ?? throw new \RuntimeException('Failed to obtain bearer token from Gitea registry.'); } }