portfolio/lib/git.ts
GeorgeWebberley 14a1ffb7b6
All checks were successful
ci/woodpecker/release/woodpecker Pipeline was successful
Made active link and active stream conditional
2026-02-03 14:04:06 +01:00

101 lines
3 KiB
TypeScript

import { FORGE_PROJECTS } from "@/data/forge";
import { ChangelogEntry, ForgeProject } from "@/types";
import "server-only";
export async function getAllForgeProjects(): Promise<ForgeProject[]> {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
return Promise.all(
FORGE_PROJECTS.map(async (metadata) => {
const changelog = await getPrivateChangelog(metadata.repoName);
// Determine if the latest entry happened in the last 30 days
const latestDate =
changelog.length > 0 ? new Date(changelog[0].date) : null;
const isRecent = latestDate ? latestDate >= thirtyDaysAgo : false;
return {
...metadata,
currentVersion:
changelog.length > 0
? `${changelog[0].version}_${metadata.status}`
: `v0.0.0_${metadata.status}`,
isRecent, // This is the new conditional flag
changelog: changelog.slice(0, 3),
};
}),
);
}
export async function getPrivateChangelog(
repoName: string,
): Promise<ChangelogEntry[]> {
const repoOwner = "georgew";
const filePath = "changelog.md";
const url = `https://git.georgew.dev/api/v1/repos/${repoOwner}/${repoName}/contents/${filePath}`;
const headers = {
headers: {
Authorization: `token ${process.env.FORGEJO_TOKEN}`,
Accept: "application/vnd.forgejo.raw",
},
next: { revalidate: 3600 },
};
try {
const response = await fetch(url, headers);
if (!response.ok) {
return [
{
version: "v0.0.0",
date: new Date().toISOString().split("T")[0],
changes: ["Documentation currently synchronizing with the Forge..."],
},
];
}
const data = await response.json();
// 2. Forgejo returns the content as a base64 string
// We need to decode it to UTF-8
const markdownText = Buffer.from(data.content, "base64").toString("utf-8");
return parseMarkdown(markdownText);
} catch (error) {
console.error("Git Fetch Error:", error);
return [];
}
}
// Simple parser for your ## [Version] - YYYY-MM-DD format
function parseMarkdown(text: string): ChangelogEntry[] {
// Split by "## " at the start of a line to isolate version blocks
const sections = text.split(/\n(?=## )/g);
return sections
.map((section) => {
const lines = section
.split("\n")
.map((l) => l.trim())
.filter(Boolean);
if (lines.length === 0) return null;
// Match the pattern: ## [Version] - YYYY-MM-DD
const headerMatch = lines[0].match(/## \[(.*?)\] - (.*)/);
if (!headerMatch) return null;
const version = headerMatch[1];
const date = headerMatch[2];
// Collect all lines starting with "- " anywhere in this version block
const changes = lines
.filter((line) => line.startsWith("- "))
.map((line) => line.replace("- ", ""));
return { version, date, changes };
})
.filter((entry): entry is ChangelogEntry => entry !== null)
.slice(0, 3);
}