diff --git a/prds/1-forgejo-platform-support.md b/prds/1-forgejo-platform-support.md index fc00cb0..81245ae 100644 --- a/prds/1-forgejo-platform-support.md +++ b/prds/1-forgejo-platform-support.md @@ -232,7 +232,7 @@ The upstream repo has 13 workflows in `.github/workflows/`. Strategy: - Files: `action.yml` (MODIFIED), `src/platform/detector.ts` (NEW) - Success: `detectPlatform()` returns "forgejo" in Forgejo runner, "github" in GitHub runner. `action.yml` doesn't error on Forgejo. -- [ ] **M2: Forgejo token handling** — Create `src/forgejo/token.ts` that returns `GITHUB_TOKEN` / `FORGEJO_TOKEN` directly (no OIDC, no Anthropic exchange). Wire into `run.ts` with an early platform branch before the existing `setupGitHubToken()` call. +- [x] **M2: Forgejo token handling** — Create `src/forgejo/token.ts` that returns `GITHUB_TOKEN` / `FORGEJO_TOKEN` directly (no OIDC, no Anthropic exchange). Wire into `run.ts` with an early platform branch before the existing `setupGitHubToken()` call. - Files: `src/forgejo/token.ts` (NEW), `src/entrypoints/run.ts` (MODIFIED — surgical addition at top of `run()`) - Depends on: M1 - Success: Token is available without OIDC on Forgejo. GitHub path unchanged. diff --git a/src/entrypoints/run.ts b/src/entrypoints/run.ts index 8afc79d..15a9aa4 100644 --- a/src/entrypoints/run.ts +++ b/src/entrypoints/run.ts @@ -12,6 +12,8 @@ import { spawn } from "child_process"; import { appendFile } from "fs/promises"; import { existsSync, readFileSync } from "fs"; import { setupGitHubToken, WorkflowValidationSkipError } from "../github/token"; +import { setupForgejoToken } from "../forgejo/token"; +import { detectPlatform } from "../platform/detector"; import { checkWritePermissions } from "../github/validation/permissions"; import { createOctokit } from "../github/api/client"; import type { Octokits } from "../github/api/client"; @@ -145,15 +147,26 @@ async function run() { `Auto-detected mode: ${modeName} for event: ${context.eventName}`, ); - try { - githubToken = await setupGitHubToken(); - } catch (error) { - if (error instanceof WorkflowValidationSkipError) { - core.setOutput("skipped_due_to_workflow_validation_mismatch", "true"); - console.log("Exiting due to workflow validation skip"); - return; + // Platform-aware token setup + const platform = detectPlatform(); + console.log(`Platform detected: ${platform}`); + + if (platform === "forgejo") { + githubToken = setupForgejoToken(); + } else { + try { + githubToken = await setupGitHubToken(); + } catch (error) { + if (error instanceof WorkflowValidationSkipError) { + core.setOutput( + "skipped_due_to_workflow_validation_mismatch", + "true", + ); + console.log("Exiting due to workflow validation skip"); + return; + } + throw error; } - throw error; } octokit = createOctokit(githubToken); diff --git a/src/forgejo/token.ts b/src/forgejo/token.ts new file mode 100644 index 0000000..ffa7f3b --- /dev/null +++ b/src/forgejo/token.ts @@ -0,0 +1,28 @@ +/** + * Forgejo token handling. + * + * Unlike GitHub (which uses OIDC → Anthropic exchange for an app token), + * Forgejo uses the runner's workflow token directly. No exchange needed. + */ + +export function setupForgejoToken(): string { + // 1. User-provided token (via github_token input → OVERRIDE_GITHUB_TOKEN) + const providedToken = process.env.OVERRIDE_GITHUB_TOKEN; + if (providedToken) { + console.log("Using provided token for Forgejo authentication"); + return providedToken; + } + + // 2. Runner's default workflow token (github.token → DEFAULT_WORKFLOW_TOKEN) + const defaultToken = process.env.DEFAULT_WORKFLOW_TOKEN; + if (defaultToken) { + console.log("Using default workflow token for Forgejo authentication"); + return defaultToken; + } + + throw new Error( + "No authentication token available for Forgejo. " + + "Provide a token via the 'github_token' input or ensure " + + "the workflow has a valid GITHUB_TOKEN.", + ); +} diff --git a/test/forgejo-token.test.ts b/test/forgejo-token.test.ts new file mode 100644 index 0000000..b3d5d6c --- /dev/null +++ b/test/forgejo-token.test.ts @@ -0,0 +1,62 @@ +import { setupForgejoToken } from "../src/forgejo/token"; +import { describe, it, expect, beforeEach } from "bun:test"; + +describe("setupForgejoToken", () => { + beforeEach(() => { + delete process.env.OVERRIDE_GITHUB_TOKEN; + delete process.env.DEFAULT_WORKFLOW_TOKEN; + }); + + describe("OVERRIDE_GITHUB_TOKEN (user-provided via github_token input)", () => { + it("should return OVERRIDE_GITHUB_TOKEN when set", () => { + process.env.OVERRIDE_GITHUB_TOKEN = "user-provided-token"; + expect(setupForgejoToken()).toBe("user-provided-token"); + }); + + it("should take precedence over DEFAULT_WORKFLOW_TOKEN", () => { + process.env.OVERRIDE_GITHUB_TOKEN = "override-token"; + process.env.DEFAULT_WORKFLOW_TOKEN = "default-token"; + expect(setupForgejoToken()).toBe("override-token"); + }); + + it("should not use empty OVERRIDE_GITHUB_TOKEN", () => { + process.env.OVERRIDE_GITHUB_TOKEN = ""; + process.env.DEFAULT_WORKFLOW_TOKEN = "default-token"; + expect(setupForgejoToken()).toBe("default-token"); + }); + }); + + describe("DEFAULT_WORKFLOW_TOKEN (runner's github.token)", () => { + it("should return DEFAULT_WORKFLOW_TOKEN when no override is set", () => { + process.env.DEFAULT_WORKFLOW_TOKEN = "workflow-token"; + expect(setupForgejoToken()).toBe("workflow-token"); + }); + + it("should not use empty DEFAULT_WORKFLOW_TOKEN", () => { + process.env.DEFAULT_WORKFLOW_TOKEN = ""; + expect(() => setupForgejoToken()).toThrow( + "No authentication token available for Forgejo", + ); + }); + }); + + describe("error handling", () => { + it("should throw when no token is available", () => { + expect(() => setupForgejoToken()).toThrow( + "No authentication token available for Forgejo", + ); + }); + + it("should throw when both env vars are empty strings", () => { + process.env.OVERRIDE_GITHUB_TOKEN = ""; + process.env.DEFAULT_WORKFLOW_TOKEN = ""; + expect(() => setupForgejoToken()).toThrow( + "No authentication token available for Forgejo", + ); + }); + + it("should include guidance in the error message", () => { + expect(() => setupForgejoToken()).toThrow("github_token"); + }); + }); +});