Output flag option and no error on missing var
- Add -o option to dictate where to output file to. - Update so that missing environment variables just skips that specific hub
This commit is contained in:
parent
277e05fa2e
commit
f4222a1dd4
1 changed files with 87 additions and 37 deletions
|
|
@ -1,19 +1,70 @@
|
||||||
function requireEnv(key: string): string {
|
import { mkdir } from "node:fs/promises";
|
||||||
const value = process.env[key];
|
import { dirname } from "node:path";
|
||||||
if (!value) throw new Error(`Missing required env var: ${key}`);
|
|
||||||
return value;
|
function parseArgs(args: string[]): { outputPath: string } {
|
||||||
|
let outputPath = "../static/activity.json";
|
||||||
|
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
const arg = args[i];
|
||||||
|
|
||||||
|
if (arg === "--help" || arg === "-h") {
|
||||||
|
console.log(
|
||||||
|
"Usage: bun run index.ts [--output <path>]\n\nOptions:\n -o, --output <path> Where to write activity.json (default: ../static/activity.json)",
|
||||||
|
);
|
||||||
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg === "--output" || arg === "-o") {
|
||||||
|
const value = args[i + 1];
|
||||||
|
if (!value) throw new Error(`${arg} requires a path`);
|
||||||
|
outputPath = value;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg?.startsWith("--output=")) {
|
||||||
|
outputPath = arg.slice("--output=".length);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unknown argument: ${arg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { outputPath };
|
||||||
|
}
|
||||||
|
|
||||||
|
const cli = parseArgs(Bun.argv.slice(2));
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
githubApiKey: requireEnv("GH_API_KEY"),
|
githubApiKey: process.env["GH_API_KEY"] ?? "",
|
||||||
forgejoApiKey: requireEnv("FJ_API_KEY"),
|
forgejoApiKey: process.env["FJ_API_KEY"] ?? "",
|
||||||
forgejoUsername: requireEnv("FJ_USER"),
|
forgejoUsername: process.env["FJ_USER"] ?? "",
|
||||||
forgejoURL: requireEnv("FJ_URL"),
|
forgejoURL: process.env["FJ_URL"] ?? "https://codeberg.org",
|
||||||
gitlabApiKey: process.env["GL_API_KEY"],
|
gitlabApiKey: process.env["GL_API_KEY"] ?? "",
|
||||||
gitlabUsername: process.env["GL_USER"],
|
gitlabUsername: process.env["GL_USER"] ?? "",
|
||||||
gitlabURL: process.env["GL_URL"] ?? "https://gitlab.com",
|
gitlabURL: process.env["GL_URL"] ?? "https://gitlab.com",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function areGhVarsPresent() {
|
||||||
|
return (
|
||||||
|
config.githubApiKey?.trim().length > 0 &&
|
||||||
|
config.githubApiKey?.trim().length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function areFjVarsPresent() {
|
||||||
|
return (
|
||||||
|
config.forgejoApiKey?.trim().length > 0 &&
|
||||||
|
config.forgejoUsername?.trim().length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function areGlVarsPresent() {
|
||||||
|
return (
|
||||||
|
config.gitlabApiKey?.trim().length > 0 &&
|
||||||
|
config.gitlabUsername?.trim().length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const oneYearAgo = new Date();
|
const oneYearAgo = new Date();
|
||||||
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
||||||
|
|
@ -142,7 +193,8 @@ function parseGitHubContributions(data: any): ActivityMap {
|
||||||
const calendarMap: { [day: string]: number } = {};
|
const calendarMap: { [day: string]: number } = {};
|
||||||
for (const week of collection?.contributionCalendar?.weeks ?? []) {
|
for (const week of collection?.contributionCalendar?.weeks ?? []) {
|
||||||
for (const day of week.contributionDays) {
|
for (const day of week.contributionDays) {
|
||||||
if (day.contributionCount > 0) calendarMap[day.date] = day.contributionCount;
|
if (day.contributionCount > 0)
|
||||||
|
calendarMap[day.date] = day.contributionCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,7 +273,7 @@ async function getForgejoActivityMap(): Promise<ActivityMap> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const activities: any[] = await response.json();
|
const activities = (await response.json()) as any[];
|
||||||
if (activities.length === 0) break;
|
if (activities.length === 0) break;
|
||||||
|
|
||||||
const pageMap = parseForgejoActivity(activities);
|
const pageMap = parseForgejoActivity(activities);
|
||||||
|
|
@ -312,7 +364,6 @@ function parseGitLabEvents(events: any[]): ActivityMap {
|
||||||
|
|
||||||
async function getGitLabActivityMap(): Promise<ActivityMap> {
|
async function getGitLabActivityMap(): Promise<ActivityMap> {
|
||||||
if (!config.gitlabApiKey || !config.gitlabUsername) {
|
if (!config.gitlabApiKey || !config.gitlabUsername) {
|
||||||
console.log("[GitLab] Skipping: GL_API_KEY or GL_USER not set");
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,35 +372,23 @@ async function getGitLabActivityMap(): Promise<ActivityMap> {
|
||||||
const map: ActivityMap = {};
|
const map: ActivityMap = {};
|
||||||
const afterDate = oneYearAgo.toISOString().split("T")[0]!;
|
const afterDate = oneYearAgo.toISOString().split("T")[0]!;
|
||||||
|
|
||||||
console.log(`[GitLab] Fetching events for user "${config.gitlabUsername}" from ${config.gitlabURL} after ${afterDate}`);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const url = `${config.gitlabURL}/api/v4/users/${config.gitlabUsername}/events?action=pushed&after=${afterDate}&per_page=${pageLimit}&page=${page}`;
|
const url = `${config.gitlabURL}/api/v4/users/${config.gitlabUsername}/events?action=pushed&after=${afterDate}&per_page=${pageLimit}&page=${page}`;
|
||||||
console.log(`[GitLab] GET ${url}`);
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: { "PRIVATE-TOKEN": config.gitlabApiKey },
|
headers: { "PRIVATE-TOKEN": config.gitlabApiKey },
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`[GitLab] Response: ${response.status} ${response.statusText}`);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const body = await response.text();
|
throw new Error(
|
||||||
console.error(`[GitLab] Error body: ${body}`);
|
`GitLab API error: ${response.status} ${response.statusText}`,
|
||||||
throw new Error(`GitLab API error: ${response.status} ${response.statusText}`);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const events: any[] = await response.json();
|
|
||||||
console.log(`[GitLab] Page ${page}: received ${events.length} events`);
|
|
||||||
|
|
||||||
if (events.length > 0) {
|
|
||||||
console.log(`[GitLab] First event sample:`, JSON.stringify(events[0], null, 2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const events = (await response.json()) as any[];
|
||||||
if (events.length === 0) break;
|
if (events.length === 0) break;
|
||||||
|
|
||||||
const parsed = parseGitLabEvents(events);
|
const parsed = parseGitLabEvents(events);
|
||||||
console.log(`[GitLab] Page ${page}: parsed ${Object.keys(parsed).length} days of activity`);
|
|
||||||
mergeInto(map, parsed);
|
mergeInto(map, parsed);
|
||||||
|
|
||||||
if (events.length < pageLimit) break;
|
if (events.length < pageLimit) break;
|
||||||
|
|
@ -360,10 +399,23 @@ async function getGitLabActivityMap(): Promise<ActivityMap> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function makeActivityMap(): Promise<ActivityMap> {
|
async function makeActivityMap(): Promise<ActivityMap> {
|
||||||
|
const getActivity = async (
|
||||||
|
varsPresentCheck: () => boolean,
|
||||||
|
getActivityMap: () => Promise<ActivityMap>,
|
||||||
|
hubName: string,
|
||||||
|
) => {
|
||||||
|
if (varsPresentCheck()) {
|
||||||
|
console.log(`Fetching ${hubName} activity`);
|
||||||
|
return getActivityMap();
|
||||||
|
} else {
|
||||||
|
console.log(`${hubName} not configured, skipping...`);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
const [ghMap, fjMap, glMap] = await Promise.all([
|
const [ghMap, fjMap, glMap] = await Promise.all([
|
||||||
getGitHubActivityMap(),
|
getActivity(areGhVarsPresent, getGitHubActivityMap, "Github"),
|
||||||
getForgejoActivityMap(),
|
getActivity(areFjVarsPresent, getForgejoActivityMap, "Forgejo"),
|
||||||
getGitLabActivityMap(),
|
getActivity(areGlVarsPresent, getGitLabActivityMap, "Gitlab"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const merged = deduplicateAcrossPlatforms(ghMap, fjMap);
|
const merged = deduplicateAcrossPlatforms(ghMap, fjMap);
|
||||||
|
|
@ -371,10 +423,8 @@ async function makeActivityMap(): Promise<ActivityMap> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const activityMap = await makeActivityMap();
|
const activityMap = await makeActivityMap();
|
||||||
await Bun.write(
|
await mkdir(dirname(cli.outputPath), { recursive: true });
|
||||||
"../static/activity.json",
|
await Bun.write(cli.outputPath, JSON.stringify(activityMap, null, 2));
|
||||||
JSON.stringify(activityMap, null, 2),
|
|
||||||
);
|
|
||||||
console.log(
|
console.log(
|
||||||
`Wrote ${Object.keys(activityMap).length} days of activity to out/activity.json`,
|
`Wrote ${Object.keys(activityMap).length} days of activity to ${cli.outputPath}`,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue