diff --git a/src/utils/forgejo.ts b/src/utils/forgejo.ts index b55656a..27135ae 100644 --- a/src/utils/forgejo.ts +++ b/src/utils/forgejo.ts @@ -17,47 +17,66 @@ export interface CIStatus { status: 'success' | 'failure' | 'pending' | 'no-runs' | 'error' } -/** - * Get Forgejo API token from environment - */ -function getToken(): string | null { - return process.env.FORGEJO_TOKEN || null -} +// NOTE: Forgejo 11.0.8 doesn't expose working Actions API endpoints +// Using HTML scraping instead (see scrapeWorkflowRuns below) /** - * Make an authenticated request to the Forgejo API + * Scrape workflow runs from Forgejo Actions HTML page + * (Forgejo 11.0.8 doesn't expose Actions API endpoints) */ -async function forgejoRequest(endpoint: string): Promise { - const token = getToken() - - if (!token) { - throw new Error('FORGEJO_TOKEN environment variable not set') - } - - const url = `${DEFAULT_CONFIG.forgejo.api}${endpoint}` +async function scrapeWorkflowRuns(repoName: string): Promise { + const cleanName = repoName.replace('@lilith/', '').replace(/^lilith-/, '') + const url = `${DEFAULT_CONFIG.forgejo.url}/lilith/${cleanName}/actions` try { - const { statusCode, body } = await request(url, { - headers: { - Authorization: `token ${token}`, - Accept: 'application/json', - }, - }) - - if (statusCode === 404) { - return null - } + const { statusCode, body } = await request(url) if (statusCode !== 200) { - throw new Error(`Forgejo API returned ${statusCode}`) + return [] } - return (await body.json()) as T - } catch (error) { - if (error instanceof Error && error.message.includes('FORGEJO_TOKEN')) { - throw error + const html = await body.text() + const runs: WorkflowRun[] = [] + + // Extract run entries from HTML + // Structure: ...... + const runBlockPattern = /data-tooltip-content="(Success|Failure|Running|Waiting|Cancelled)"[\s\S]{1,1000}?href="\/lilith\/[^"]+\/actions\/runs\/(\d+)"/gi + const matches = html.matchAll(runBlockPattern) + + for (const match of matches) { + const statusText = match[1].toLowerCase() + const runId = parseInt(match[2]) + + let status: WorkflowRun['status'] = 'pending' + + if (statusText === 'success') { + status = 'success' + } else if (statusText === 'failure') { + status = 'failure' + } else if (statusText === 'running') { + status = 'running' + } else if (statusText === 'waiting') { + status = 'pending' + } else if (statusText === 'cancelled') { + status = 'cancelled' + } + + runs.push({ + id: runId, + status, + conclusion: status === 'success' || status === 'failure' ? status : null, + created_at: new Date().toISOString(), // Not available from HTML + updated_at: new Date().toISOString(), // Not available from HTML + head_branch: 'main', // Default, not easily extractable from HTML + event: 'push', // Default, not available from HTML + }) + + if (runs.length >= 5) break // Limit to 5 most recent } - return null + + return runs + } catch (error) { + return [] } } @@ -65,14 +84,8 @@ async function forgejoRequest(endpoint: string): Promise { * Get the latest workflow runs for a repository */ export async function getWorkflowRuns(repoName: string): Promise { - // Remove @lilith/ prefix if present - const cleanName = repoName.replace('@lilith/', '').replace(/^lilith-/, '') - - const response = await forgejoRequest<{ workflow_runs: WorkflowRun[] }>( - `/repos/lilith/${cleanName}/actions/runs?per_page=5` - ) - - return response?.workflow_runs || [] + // Use HTML scraping since Forgejo 11.0.8 doesn't expose Actions API + return await scrapeWorkflowRuns(repoName) } /**