basic infra for tag support

This commit is contained in:
Emil Lerch 2025-07-18 07:26:08 -07:00
parent f25ce7e510
commit 68297f4389
Signed by: lobo
GPG key ID: A7B62D657EF764F8
9 changed files with 29 additions and 0 deletions

View file

@ -190,6 +190,9 @@ pub fn generateFeed(allocator: Allocator, releases: []const Release) ![]u8 {
try escapeXml(writer, release.repo_name); try escapeXml(writer, release.repo_name);
try writer.writeAll(" - "); try writer.writeAll(" - ");
try escapeXml(writer, release.tag_name); try escapeXml(writer, release.tag_name);
if (release.is_tag) {
try writer.writeAll(" (tag)");
}
try writer.writeAll("</title>\n"); try writer.writeAll("</title>\n");
try writer.writeAll(" <link href=\""); try writer.writeAll(" <link href=\"");
@ -289,6 +292,7 @@ test "Atom feed generation with markdown" {
.html_url = "https://github.com/test/repo/releases/tag/v1.0.0", .html_url = "https://github.com/test/repo/releases/tag/v1.0.0",
.description = "## What's Changed\n* Fixed bug\n* Added feature", .description = "## What's Changed\n* Fixed bug\n* Added feature",
.provider = "github", .provider = "github",
.is_tag = false,
}, },
}; };
@ -317,6 +321,7 @@ test "Atom feed with fenced code blocks" {
.html_url = "https://github.com/test/repo/releases/tag/v1.0.0", .html_url = "https://github.com/test/repo/releases/tag/v1.0.0",
.description = "Here's some code:\n```javascript\nconst greeting = 'Hello World';\nconsole.log(greeting);\n```\nEnd of example.", .description = "Here's some code:\n```javascript\nconst greeting = 'Hello World';\nconsole.log(greeting);\n```\nEnd of example.",
.provider = "github", .provider = "github",
.is_tag = false,
}, },
}; };
@ -348,6 +353,7 @@ test "Atom feed with fallback markdown" {
.html_url = "https://github.com/test/repo/releases/tag/v1.0.0", .html_url = "https://github.com/test/repo/releases/tag/v1.0.0",
.description = "| Column 1 | Column 2 |\n|----------|----------|\n| Value 1 | Value 2 |", .description = "| Column 1 | Column 2 |\n|----------|----------|\n| Value 1 | Value 2 |",
.provider = "github", .provider = "github",
.is_tag = false,
}, },
}; };
@ -373,6 +379,7 @@ test "Atom feed with special characters" {
.html_url = "https://github.com/test/repo/releases/tag/v1.0.0", .html_url = "https://github.com/test/repo/releases/tag/v1.0.0",
.description = "Test \"release\" with <special> chars & symbols", .description = "Test \"release\" with <special> chars & symbols",
.provider = "github", .provider = "github",
.is_tag = false,
}, },
}; };

View file

@ -28,6 +28,7 @@ test "Atom feed validates against W3C validator" {
.html_url = "https://github.com/ziglang/zig/releases/tag/0.14.0", .html_url = "https://github.com/ziglang/zig/releases/tag/0.14.0",
.description = "Zig 0.14.0 release with many improvements", .description = "Zig 0.14.0 release with many improvements",
.provider = "github", .provider = "github",
.is_tag = false,
}, },
Release{ Release{
.repo_name = "example/test", .repo_name = "example/test",
@ -36,6 +37,7 @@ test "Atom feed validates against W3C validator" {
.html_url = "https://github.com/example/test/releases/tag/v1.2.3", .html_url = "https://github.com/example/test/releases/tag/v1.2.3",
.description = "Bug fixes and performance improvements", .description = "Bug fixes and performance improvements",
.provider = "github", .provider = "github",
.is_tag = false,
}, },
}; };

View file

@ -66,6 +66,7 @@ pub const Release = struct {
html_url: []const u8, html_url: []const u8,
description: []const u8, description: []const u8,
provider: []const u8, provider: []const u8,
is_tag: bool = false,
pub fn deinit(self: Release, allocator: Allocator) void { pub fn deinit(self: Release, allocator: Allocator) void {
allocator.free(self.repo_name); allocator.free(self.repo_name);
@ -363,6 +364,7 @@ test "atom feed generation" {
.html_url = "https://github.com/test/repo/releases/tag/v1.0.0", .html_url = "https://github.com/test/repo/releases/tag/v1.0.0",
.description = "Test release", .description = "Test release",
.provider = "github", .provider = "github",
.is_tag = false,
}, },
}; };
@ -427,6 +429,7 @@ test "Age-based release filtering" {
.html_url = "https://github.com/test/recent/releases/tag/v1.0.0", .html_url = "https://github.com/test/recent/releases/tag/v1.0.0",
.description = "Recent release", .description = "Recent release",
.provider = "github", .provider = "github",
.is_tag = false,
}; };
const old_release = Release{ const old_release = Release{
@ -436,6 +439,7 @@ test "Age-based release filtering" {
.html_url = "https://github.com/test/old/releases/tag/v0.1.0", .html_url = "https://github.com/test/old/releases/tag/v0.1.0",
.description = "Old release", .description = "Old release",
.provider = "github", .provider = "github",
.is_tag = false,
}; };
const borderline_release = Release{ const borderline_release = Release{
@ -445,6 +449,7 @@ test "Age-based release filtering" {
.html_url = "https://github.com/test/borderline/releases/tag/v0.5.0", .html_url = "https://github.com/test/borderline/releases/tag/v0.5.0",
.description = "Borderline release", .description = "Borderline release",
.provider = "github", .provider = "github",
.is_tag = false,
}; };
const releases = [_]Release{ recent_release, old_release, borderline_release }; const releases = [_]Release{ recent_release, old_release, borderline_release };

View file

@ -37,6 +37,7 @@ pub fn fetchReleases(self: *Self, allocator: Allocator) !ArrayList(Release) {
// Get releases for each repo // Get releases for each repo
for (starred_repos.items) |repo| { for (starred_repos.items) |repo| {
// TODO: Investigate the tags/releases situation similar to GitHub
const repo_releases = getRepoReleases(allocator, &client, self.token, repo) catch |err| { const repo_releases = getRepoReleases(allocator, &client, self.token, repo) catch |err| {
const stderr = std.io.getStdErr().writer(); const stderr = std.io.getStdErr().writer();
stderr.print("Error fetching Codeberg releases for {s}: {}\n", .{ repo, err }) catch {}; stderr.print("Error fetching Codeberg releases for {s}: {}\n", .{ repo, err }) catch {};
@ -239,6 +240,7 @@ fn getRepoReleases(allocator: Allocator, client: *http.Client, token: []const u8
.html_url = try allocator.dupe(u8, html_url_value.string), .html_url = try allocator.dupe(u8, html_url_value.string),
.description = try allocator.dupe(u8, body_str), .description = try allocator.dupe(u8, body_str),
.provider = try allocator.dupe(u8, "codeberg"), .provider = try allocator.dupe(u8, "codeberg"),
.is_tag = false,
}; };
releases.append(release) catch |err| { releases.append(release) catch |err| {
@ -317,6 +319,7 @@ test "codeberg release parsing with live data snapshot" {
.html_url = try allocator.dupe(u8, html_url_value.string), .html_url = try allocator.dupe(u8, html_url_value.string),
.description = try allocator.dupe(u8, body_str), .description = try allocator.dupe(u8, body_str),
.provider = try allocator.dupe(u8, "codeberg"), .provider = try allocator.dupe(u8, "codeberg"),
.is_tag = false,
}; };
try releases.append(release); try releases.append(release);

View file

@ -414,6 +414,7 @@ fn getRepoReleases(allocator: Allocator, client: *http.Client, token: []const u8
.html_url = try allocator.dupe(u8, obj.get("html_url").?.string), .html_url = try allocator.dupe(u8, obj.get("html_url").?.string),
.description = try allocator.dupe(u8, body_str), .description = try allocator.dupe(u8, body_str),
.provider = try allocator.dupe(u8, "github"), .provider = try allocator.dupe(u8, "github"),
.is_tag = false,
}; };
try releases.append(release); try releases.append(release);
@ -564,6 +565,7 @@ test "github release parsing with live data snapshot" {
.html_url = try allocator.dupe(u8, obj.get("html_url").?.string), .html_url = try allocator.dupe(u8, obj.get("html_url").?.string),
.description = try allocator.dupe(u8, body_str), .description = try allocator.dupe(u8, body_str),
.provider = try allocator.dupe(u8, "github"), .provider = try allocator.dupe(u8, "github"),
.is_tag = false,
}; };
try releases.append(release); try releases.append(release);

View file

@ -36,6 +36,7 @@ pub fn fetchReleases(self: *Self, allocator: Allocator) !ArrayList(Release) {
} }
// Get releases for each project // Get releases for each project
// TODO: Investigate tags similar to GitHub
for (starred_projects.items) |project_id| { for (starred_projects.items) |project_id| {
const project_releases = getProjectReleases(allocator, &client, self.token, project_id) catch |err| { const project_releases = getProjectReleases(allocator, &client, self.token, project_id) catch |err| {
const stderr = std.io.getStdErr().writer(); const stderr = std.io.getStdErr().writer();
@ -226,6 +227,7 @@ fn getProjectReleases(allocator: Allocator, client: *http.Client, token: []const
.html_url = try allocator.dupe(u8, obj.get("_links").?.object.get("self").?.string), .html_url = try allocator.dupe(u8, obj.get("_links").?.object.get("self").?.string),
.description = try allocator.dupe(u8, desc_str), .description = try allocator.dupe(u8, desc_str),
.provider = try allocator.dupe(u8, "gitlab"), .provider = try allocator.dupe(u8, "gitlab"),
.is_tag = false,
}; };
releases.append(release) catch |err| { releases.append(release) catch |err| {
@ -322,6 +324,7 @@ test "gitlab release parsing with live data snapshot" {
.html_url = try allocator.dupe(u8, obj.get("_links").?.object.get("self").?.string), .html_url = try allocator.dupe(u8, obj.get("_links").?.object.get("self").?.string),
.description = try allocator.dupe(u8, desc_str), .description = try allocator.dupe(u8, desc_str),
.provider = try allocator.dupe(u8, "gitlab"), .provider = try allocator.dupe(u8, "gitlab"),
.is_tag = false,
}; };
try releases.append(release); try releases.append(release);

View file

@ -150,6 +150,7 @@ fn fetchReleasesMultiRepo(allocator: Allocator, client: *http.Client, token: []c
else else
"1970-01-01T00:00:00Z"; "1970-01-01T00:00:00Z";
// TODO: Investigate annotated tags as the description here
const release = Release{ const release = Release{
.repo_name = try std.fmt.allocPrint(allocator, "~{s}/{s}", .{ tag_data.username, tag_data.reponame }), .repo_name = try std.fmt.allocPrint(allocator, "~{s}/{s}", .{ tag_data.username, tag_data.reponame }),
.tag_name = try allocator.dupe(u8, tag_data.tag_name), .tag_name = try allocator.dupe(u8, tag_data.tag_name),
@ -157,6 +158,7 @@ fn fetchReleasesMultiRepo(allocator: Allocator, client: *http.Client, token: []c
.html_url = try std.fmt.allocPrint(allocator, "https://git.sr.ht/~{s}/{s}/refs/{s}", .{ tag_data.username, tag_data.reponame, tag_data.tag_name }), .html_url = try std.fmt.allocPrint(allocator, "https://git.sr.ht/~{s}/{s}/refs/{s}", .{ tag_data.username, tag_data.reponame, tag_data.tag_name }),
.description = try std.fmt.allocPrint(allocator, "Tag {s} (commit: {s})", .{ tag_data.tag_name, tag_data.commit_id }), .description = try std.fmt.allocPrint(allocator, "Tag {s} (commit: {s})", .{ tag_data.tag_name, tag_data.commit_id }),
.provider = try allocator.dupe(u8, "sourcehut"), .provider = try allocator.dupe(u8, "sourcehut"),
.is_tag = false, // Well, this is a lie. However, sourcehut doesn't have "releases", so it is a little weird to always set this true
}; };
releases.append(release) catch |err| { releases.append(release) catch |err| {

View file

@ -79,6 +79,7 @@ test "compareReleasesByDate with various timestamp formats" {
.html_url = "https://github.com/test/iso-early/releases/tag/v1.0.0", .html_url = "https://github.com/test/iso-early/releases/tag/v1.0.0",
.description = "Early ISO format", .description = "Early ISO format",
.provider = "github", .provider = "github",
.is_tag = false,
}; };
const release_iso_late = Release{ const release_iso_late = Release{
@ -91,6 +92,7 @@ test "compareReleasesByDate with various timestamp formats" {
.html_url = "https://github.com/test/iso-late/releases/tag/v2.0.0", .html_url = "https://github.com/test/iso-late/releases/tag/v2.0.0",
.description = "Late ISO format", .description = "Late ISO format",
.provider = "github", .provider = "github",
.is_tag = false,
}; };
const release_invalid = Release{ const release_invalid = Release{
@ -100,6 +102,7 @@ test "compareReleasesByDate with various timestamp formats" {
.html_url = "https://github.com/test/invalid/releases/tag/v3.0.0", .html_url = "https://github.com/test/invalid/releases/tag/v3.0.0",
.description = "Invalid format", .description = "Invalid format",
.provider = "github", .provider = "github",
.is_tag = false,
}; };
// Later date should come before earlier date (more recent first) // Later date should come before earlier date (more recent first)

View file

@ -53,6 +53,7 @@ test "compareReleasesByDate" {
.html_url = "https://github.com/test/repo1/releases/tag/v1.0.0", .html_url = "https://github.com/test/repo1/releases/tag/v1.0.0",
.description = "First release", .description = "First release",
.provider = "github", .provider = "github",
.is_tag = false,
}; };
const release2 = Release{ const release2 = Release{
@ -65,6 +66,7 @@ test "compareReleasesByDate" {
.html_url = "https://github.com/test/repo2/releases/tag/v2.0.0", .html_url = "https://github.com/test/repo2/releases/tag/v2.0.0",
.description = "Second release", .description = "Second release",
.provider = "github", .provider = "github",
.is_tag = false,
}; };
// release2 should come before release1 (more recent first) // release2 should come before release1 (more recent first)