Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 49 additions & 82 deletions scripts/fetch-feeds.js
Original file line number Diff line number Diff line change
@@ -1,109 +1,76 @@
const fs = require("fs");
const path = require("path");
const { parseString } = require("xml2js");

async function fetchAndParseFeed(url, filename) {
async function fetchReleases(repo, filename) {
try {
const fetch = (await import("node-fetch")).default;
const url = `https://api.github.com/repos/${repo}/releases?per_page=20`;
console.log(`Fetching ${url}...`);

const response = await fetch(url);
const xmlText = await response.text();
const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
const headers = {
"User-Agent": "bluefin-docs",
};

// Parse XML to JSON
return new Promise((resolve, reject) => {
parseString(xmlText, (err, result) => {
if (err) {
console.error(`Error parsing XML from ${url}:`, err);
reject(err);
return;
}
if (token) {
headers["Authorization"] = `token ${token}`;
} else {
console.warn("No GITHUB_TOKEN or GH_TOKEN found. Rate limits may apply.");
}

// Extract feed entries and convert to simple format
const feed = result.feed;
const entries = feed.entry || [];
const response = await fetch(url, { headers });

const releases = entries.map((entry) => {
// Find the HTML link
let link = "#";
if (entry.link && Array.isArray(entry.link)) {
const htmlLink = entry.link.find(
(l) => l.$ && l.$.type === "text/html",
);
link = htmlLink ? htmlLink.$.href : entry.link[0].$.href;
}
if (!response.ok) {
throw new Error(`GitHub API responded with ${response.status}: ${response.statusText}`);
}

// Handle content safely
let content = "";
let contentSnippet = "";
if (
entry.content &&
Array.isArray(entry.content) &&
entry.content[0]
) {
if (typeof entry.content[0] === "string") {
content = entry.content[0];
contentSnippet = content.substring(0, 200) + "...";
} else if (
entry.content[0]._ &&
typeof entry.content[0]._ === "string"
) {
content = entry.content[0]._;
contentSnippet = content.substring(0, 200) + "...";
}
}
const data = await response.json();

return {
title: entry.title ? entry.title[0] : "Unknown Release",
link,
pubDate: entry.updated ? entry.updated[0] : "",
contentSnippet,
content,
};
});
const releases = data.map((release) => {
return {
title: release.name || release.tag_name,
link: release.html_url,
pubDate: release.published_at,
contentSnippet: release.body ? release.body.substring(0, 200) + "..." : "",
content: release.body || "",
id: release.id.toString(),
};
});

const feedsDir = path.join(__dirname, "..", "static", "feeds");
if (!fs.existsSync(feedsDir)) {
fs.mkdirSync(feedsDir, { recursive: true });
}
const feedsDir = path.join(__dirname, "..", "static", "feeds");
if (!fs.existsSync(feedsDir)) {
fs.mkdirSync(feedsDir, { recursive: true });
}

// Save as JSON
const jsonPath = path.join(feedsDir, filename.replace(".xml", ".json"));
fs.writeFileSync(
jsonPath,
JSON.stringify(
{
title: feed.title ? feed.title[0] : "Releases",
items: releases,
},
null,
2,
),
);
// Save as JSON
const jsonPath = path.join(feedsDir, filename.replace(".xml", ".json"));
fs.writeFileSync(
jsonPath,
JSON.stringify(
{
title: `Releases for ${repo}`,
items: releases,
},
null,
2,
),
);

console.log(`Converted and saved to ${jsonPath}`);
resolve(releases);
});
});
console.log(`Saved ${releases.length} items to ${jsonPath}`);
return releases;
} catch (error) {
console.error(`Error fetching ${url}:`, error);
console.error(`Error fetching releases for ${repo}:`, error);
return null;
}
}

async function main() {
await fetchAndParseFeed(
"https://github.com/ublue-os/bluefin/releases.atom",
"bluefin-releases.xml",
);
await fetchAndParseFeed(
"https://github.com/ublue-os/bluefin-lts/releases.atom",
"bluefin-lts-releases.xml",
);
await fetchReleases("ublue-os/bluefin", "bluefin-releases.json");
await fetchReleases("ublue-os/bluefin-lts", "bluefin-lts-releases.json");
}

if (require.main === module) {
main().catch(console.error);
}

module.exports = { fetchAndParseFeed };
module.exports = { fetchReleases };
Loading