cloudflare/kumo
Publicmirrored fromhttps://github.com/cloudflare/kumoAvailable
ci/reporters/types.ts
143lines · modecode
| 1 | /** |
| 2 | * Types for the PR comment reporter system |
| 3 | * |
| 4 | * This system collects report items from multiple CI jobs and consolidates |
| 5 | * them into a single PR comment. Each job outputs a report artifact that |
| 6 | * is collected by the final reporter job. |
| 7 | */ |
| 8 | |
| 9 | import { |
| 10 | existsSync, |
| 11 | mkdirSync, |
| 12 | writeFileSync, |
| 13 | readFileSync, |
| 14 | readdirSync, |
| 15 | } from "node:fs"; |
| 16 | import { join } from "node:path"; |
| 17 | |
| 18 | /** Directory where report artifacts are stored */ |
| 19 | export const REPORTS_DIR = "ci/reports"; |
| 20 | |
| 21 | /** |
| 22 | * A single item to be included in the PR comment |
| 23 | */ |
| 24 | export interface ReportItem { |
| 25 | /** Unique identifier for this report type (e.g., "npm-release", "kumo-docs-preview") */ |
| 26 | id: string; |
| 27 | /** Section title displayed in the comment */ |
| 28 | title: string; |
| 29 | /** |
| 30 | * Sort order - lower numbers appear first in comment |
| 31 | * 10-19: release info (npm) |
| 32 | * 20-29: previews (docs) |
| 33 | */ |
| 34 | priority: number; |
| 35 | /** Markdown content for this section */ |
| 36 | content: string; |
| 37 | /** Whether this item represents a successful operation */ |
| 38 | success: boolean; |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * Context available to reporters from CI environment |
| 43 | */ |
| 44 | export interface CIContext { |
| 45 | /** Full commit SHA */ |
| 46 | commitSha: string; |
| 47 | /** Short commit SHA (first 8 characters) */ |
| 48 | shortSha: string; |
| 49 | /** Pull request number */ |
| 50 | prNumber: string; |
| 51 | /** Repository name (owner/repo) */ |
| 52 | repository: string; |
| 53 | /** GitHub API token */ |
| 54 | apiToken: string; |
| 55 | /** Package name being released */ |
| 56 | packageName: string; |
| 57 | /** Package version being released */ |
| 58 | packageVersion: string; |
| 59 | /** Kumo docs preview URL (if deployed) */ |
| 60 | kumoDocsPreviewUrl?: string; |
| 61 | /** Allow additional context to be passed */ |
| 62 | [key: string]: string | undefined; |
| 63 | } |
| 64 | |
| 65 | /** |
| 66 | * Interface for reporter implementations |
| 67 | */ |
| 68 | export interface Reporter { |
| 69 | /** Unique identifier matching ReportItem.id */ |
| 70 | id: string; |
| 71 | /** Human-readable name for logging */ |
| 72 | name: string; |
| 73 | /** |
| 74 | * Collect report data from the CI context |
| 75 | * Return null if this reporter should be skipped |
| 76 | */ |
| 77 | collect(context: CIContext): Promise<ReportItem | null>; |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * Build CI context from environment variables |
| 82 | * Uses GitHub Actions environment variables |
| 83 | */ |
| 84 | export function buildContextFromEnv(): CIContext { |
| 85 | const commitSha = process.env.GITHUB_SHA ?? ""; |
| 86 | return { |
| 87 | commitSha, |
| 88 | shortSha: commitSha.substring(0, 8), |
| 89 | prNumber: process.env.GITHUB_PR_NUMBER ?? process.env.PR_NUMBER ?? "", |
| 90 | repository: process.env.GITHUB_REPOSITORY ?? "cloudflare/kumo", |
| 91 | apiToken: process.env.GITHUB_TOKEN ?? "", |
| 92 | packageName: process.env.PACKAGE_NAME ?? "@cloudflare/kumo", |
| 93 | packageVersion: process.env.PACKAGE_VERSION ?? "", |
| 94 | kumoDocsPreviewUrl: process.env.KUMO_DOCS_PREVIEW_URL, |
| 95 | }; |
| 96 | } |
| 97 | |
| 98 | /** |
| 99 | * Write a report item to the artifacts directory |
| 100 | * Called by individual CI jobs to output their report data |
| 101 | */ |
| 102 | export function writeReportArtifact(item: ReportItem): void { |
| 103 | if (!existsSync(REPORTS_DIR)) { |
| 104 | mkdirSync(REPORTS_DIR, { recursive: true }); |
| 105 | } |
| 106 | const filePath = join(REPORTS_DIR, `${item.id}.json`); |
| 107 | writeFileSync(filePath, JSON.stringify(item, null, 2)); |
| 108 | console.log(`Report artifact written: ${filePath}`); |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * Result of reading report artifacts |
| 113 | */ |
| 114 | export interface ReadReportResult { |
| 115 | items: ReportItem[]; |
| 116 | failures: string[]; |
| 117 | } |
| 118 | |
| 119 | /** |
| 120 | * Read all report artifacts from the artifacts directory |
| 121 | * Called by the final reporter job to collect all reports |
| 122 | */ |
| 123 | export function readReportArtifacts(): ReadReportResult { |
| 124 | if (!existsSync(REPORTS_DIR)) { |
| 125 | return { items: [], failures: [] }; |
| 126 | } |
| 127 | |
| 128 | const files = readdirSync(REPORTS_DIR).filter((f) => f.endsWith(".json")); |
| 129 | const items: ReportItem[] = []; |
| 130 | const failures: string[] = []; |
| 131 | |
| 132 | for (const file of files) { |
| 133 | try { |
| 134 | const content = readFileSync(join(REPORTS_DIR, file), "utf-8"); |
| 135 | items.push(JSON.parse(content) as ReportItem); |
| 136 | } catch (error) { |
| 137 | console.warn(` Failed to read report artifact: ${file}`, error); |
| 138 | failures.push(file); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | return { items, failures }; |
| 143 | } |
| 144 | |