mirror of
https://github.com/logos-co/devrel-station.git
synced 2026-08-27 19:31:11 +00:00
Milestone cards show tracking data only (status, payout, dates, reviewer, deliverables, links). Review discussion happens on the proposal issue on GitHub; the app no longer fetches comments or posts reviews — drops ReviewComposer, postReviewComment, fetchMilestoneReviews, and the MilestoneReview comment-parsing conventions. The PAT is now only needed for filing feedback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
79 lines
2.6 KiB
TypeScript
79 lines
2.6 KiB
TypeScript
import { FEEDBACK_LABEL, FEEDBACK_ORG, FEEDBACK_REPO } from "./config";
|
|
import type { FeedbackIssue } from "./types";
|
|
|
|
const API = "https://api.github.com";
|
|
|
|
function apiHeaders(): Record<string, string> {
|
|
const headers: Record<string, string> = {
|
|
Accept: "application/vnd.github+json",
|
|
"User-Agent": "devrel-station",
|
|
};
|
|
if (process.env.GITHUB_TOKEN) {
|
|
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
}
|
|
return headers;
|
|
}
|
|
|
|
interface GhSearchIssue {
|
|
number: number;
|
|
title: string;
|
|
body?: string | null;
|
|
html_url: string;
|
|
state: string;
|
|
created_at: string;
|
|
comments: number;
|
|
user: { login: string };
|
|
labels: { name: string }[];
|
|
repository_url: string; // https://api.github.com/repos/<owner>/<repo>
|
|
}
|
|
|
|
export function mapFeedbackIssue(it: GhSearchIssue): FeedbackIssue {
|
|
return {
|
|
repo: it.repository_url.replace(`${API}/repos/`, ""),
|
|
number: it.number,
|
|
title: it.title,
|
|
body: it.body ?? "",
|
|
url: it.html_url,
|
|
state: it.state === "closed" ? "closed" : "open",
|
|
author: it.user.login,
|
|
created_at: it.created_at,
|
|
labels: it.labels.map((l) => l.name).filter((n) => n !== FEEDBACK_LABEL),
|
|
comments: it.comments,
|
|
};
|
|
}
|
|
|
|
async function searchFeedbackIssues(scope: string): Promise<FeedbackIssue[] | null> {
|
|
const q = encodeURIComponent(`${scope} is:issue label:${FEEDBACK_LABEL}`);
|
|
try {
|
|
const issues: FeedbackIssue[] = [];
|
|
for (let page = 1; page <= 3; page++) {
|
|
const res = await fetch(
|
|
`${API}/search/issues?q=${q}&sort=created&order=desc&per_page=100&page=${page}`,
|
|
{ headers: apiHeaders() },
|
|
);
|
|
if (!res.ok) return page === 1 ? null : issues;
|
|
const data: { items?: GhSearchIssue[] } = await res.json();
|
|
const items = data.items ?? [];
|
|
issues.push(...items.map(mapFeedbackIssue));
|
|
if (items.length < 100) break;
|
|
}
|
|
return issues;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// All `feedback`-labeled issues across the org (plus FEEDBACK_REPO, if that
|
|
// lives outside the org), newest first. Search only sees public repos unless
|
|
// GITHUB_TOKEN grants more. Null = fetch failed.
|
|
export async function fetchFeedbackIssues(): Promise<FeedbackIssue[] | null> {
|
|
const org = await searchFeedbackIssues(`org:${FEEDBACK_ORG}`);
|
|
if (FEEDBACK_REPO.startsWith(`${FEEDBACK_ORG}/`)) return org;
|
|
const repo = await searchFeedbackIssues(`repo:${FEEDBACK_REPO}`);
|
|
if (org === null && repo === null) return null;
|
|
const merged = [...(org ?? []), ...(repo ?? [])];
|
|
return merged
|
|
.filter((i, idx) => merged.findIndex((j) => j.url === i.url) === idx)
|
|
.sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
}
|