pagination support for forgejo

This commit is contained in:
Emil Lerch 2025-07-20 15:23:49 -07:00
parent 02584db325
commit 981a46da54
Signed by: lobo
GPG key ID: A7B62D657EF764F8

View file

@ -165,14 +165,25 @@ fn getRepoReleases(allocator: Allocator, client: *http.Client, base_url: []const
releases.deinit(); releases.deinit();
} }
const url = try std.fmt.allocPrint(allocator, "{s}/api/v1/repos/{s}/releases", .{ base_url, repo }); // Normalize base_url by removing trailing slash if present
defer allocator.free(url); const normalized_base_url = if (std.mem.endsWith(u8, base_url, "/"))
base_url[0 .. base_url.len - 1]
const uri = try std.Uri.parse(url); else
base_url;
const auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{token}); const auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{token});
defer allocator.free(auth_header); defer allocator.free(auth_header);
// Paginate through all releases
var page: u32 = 1;
const per_page: u32 = 100;
while (true) {
const url = try std.fmt.allocPrint(allocator, "{s}/api/v1/repos/{s}/releases?limit={d}&page={d}", .{ normalized_base_url, repo, per_page, page });
defer allocator.free(url);
const uri = try std.Uri.parse(url);
var server_header_buffer: [16 * 1024]u8 = undefined; var server_header_buffer: [16 * 1024]u8 = undefined;
var req = try client.open(.GET, uri, .{ var req = try client.open(.GET, uri, .{
.server_header_buffer = &server_header_buffer, .server_header_buffer = &server_header_buffer,
@ -188,19 +199,15 @@ fn getRepoReleases(allocator: Allocator, client: *http.Client, base_url: []const
if (req.response.status != .ok) { if (req.response.status != .ok) {
if (req.response.status == .unauthorized) { if (req.response.status == .unauthorized) {
const stderr = std.io.getStdErr().writer();
stderr.print("Forgejo API: Unauthorized for repo {s} - check your token and scopes\n", .{repo}) catch {}; stderr.print("Forgejo API: Unauthorized for repo {s} - check your token and scopes\n", .{repo}) catch {};
return error.Unauthorized; return error.Unauthorized;
} else if (req.response.status == .forbidden) { } else if (req.response.status == .forbidden) {
const stderr = std.io.getStdErr().writer();
stderr.print("Forgejo API: Forbidden for repo {s} - token may lack required scopes\n", .{repo}) catch {}; stderr.print("Forgejo API: Forbidden for repo {s} - token may lack required scopes\n", .{repo}) catch {};
return error.Forbidden; return error.Forbidden;
} else if (req.response.status == .not_found) { } else if (req.response.status == .not_found) {
const stderr = std.io.getStdErr().writer();
stderr.print("Forgejo API: Repository {s} not found or no releases\n", .{repo}) catch {}; stderr.print("Forgejo API: Repository {s} not found or no releases\n", .{repo}) catch {};
return error.NotFound; return error.NotFound;
} }
const stderr = std.io.getStdErr().writer();
stderr.print("Forgejo API request failed for repo {s} with status: {}\n", .{ repo, req.response.status }) catch {}; stderr.print("Forgejo API request failed for repo {s} with status: {}\n", .{ repo, req.response.status }) catch {};
return error.HttpRequestFailed; return error.HttpRequestFailed;
} }
@ -209,7 +216,6 @@ fn getRepoReleases(allocator: Allocator, client: *http.Client, base_url: []const
defer allocator.free(body); defer allocator.free(body);
const parsed = json.parseFromSlice(json.Value, allocator, body, .{}) catch |err| { const parsed = json.parseFromSlice(json.Value, allocator, body, .{}) catch |err| {
const stderr = std.io.getStdErr().writer();
stderr.print("Error parsing Forgejo releases JSON for {s}: {}\n", .{ repo, err }) catch {}; stderr.print("Error parsing Forgejo releases JSON for {s}: {}\n", .{ repo, err }) catch {};
return error.JsonParseError; return error.JsonParseError;
}; };
@ -220,6 +226,12 @@ fn getRepoReleases(allocator: Allocator, client: *http.Client, base_url: []const
} }
const array = parsed.value.array; const array = parsed.value.array;
// If we got no results, we've reached the end
if (array.items.len == 0) {
break;
}
for (array.items) |item| { for (array.items) |item| {
if (item != .object) continue; if (item != .object) continue;
const obj = item.object; const obj = item.object;
@ -261,6 +273,14 @@ fn getRepoReleases(allocator: Allocator, client: *http.Client, base_url: []const
}; };
} }
// If we got fewer results than requested, we've reached the end
if (array.items.len < per_page) {
break;
}
page += 1;
}
// Sort releases by date (most recent first) // Sort releases by date (most recent first)
std.mem.sort(Release, releases.items, {}, utils.compareReleasesByDate); std.mem.sort(Release, releases.items, {}, utils.compareReleasesByDate);