From 0e53a976198559bb4e947a9d29d41a8222c50efd Mon Sep 17 00:00:00 2001 From: David Blass Date: Tue, 11 Nov 2025 18:15:51 -0500 Subject: [PATCH] improve github_token flow --- entry.js | 28 +++++++++++++++++++++------- main.ts | 16 +++++++++++++--- package.json | 2 +- utils/api.ts | 13 +++++++++++-- utils/github.ts | 9 +++++---- 5 files changed, 51 insertions(+), 17 deletions(-) diff --git a/entry.js b/entry.js index 69088a6..4eaea70 100755 --- a/entry.js +++ b/entry.js @@ -40481,7 +40481,7 @@ function query({ // package.json var package_default = { name: "@pullfrog/action", - version: "0.0.95", + version: "0.0.96", type: "module", files: [ "index.js", @@ -41084,7 +41084,7 @@ async function setupGitHubInstallationToken() { if (existingToken) { core2.setSecret(existingToken); log.info("Using provided GitHub installation token"); - return { githubInstallationToken: existingToken, wasAcquired: false }; + return { githubInstallationToken: existingToken, wasAcquired: false, isFallbackToken: false }; } const acquiredToken = await acquireNewToken(); if (!acquiredToken) { @@ -41097,11 +41097,11 @@ async function setupGitHubInstallationToken() { log.info("Using GITHUB_TOKEN (app not installed or token exchange failed)"); core2.setSecret(defaultToken); process.env.GITHUB_INSTALLATION_TOKEN = defaultToken; - return { githubInstallationToken: defaultToken, wasAcquired: false }; + return { githubInstallationToken: defaultToken, wasAcquired: false, isFallbackToken: true }; } core2.setSecret(acquiredToken); process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken; - return { githubInstallationToken: acquiredToken, wasAcquired: true }; + return { githubInstallationToken: acquiredToken, wasAcquired: true, isFallbackToken: false }; } async function revokeInstallationToken(token) { const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com"; @@ -41437,6 +41437,9 @@ var DEFAULT_REPO_SETTINGS = { }; async function getRepoSettings(token, repoContext) { const apiUrl = process.env.API_URL || "https://pullfrog.ai"; + const timeoutMs = 5e3; + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch( `${apiUrl}/api/repo/${repoContext.owner}/${repoContext.name}/settings`, @@ -41445,9 +41448,11 @@ async function getRepoSettings(token, repoContext) { headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" - } + }, + signal: controller.signal } ); + clearTimeout(timeoutId); if (!response.ok) { return DEFAULT_REPO_SETTINGS; } @@ -41457,6 +41462,7 @@ async function getRepoSettings(token, repoContext) { } return settings; } catch { + clearTimeout(timeoutId); return DEFAULT_REPO_SETTINGS; } } @@ -41504,12 +41510,20 @@ async function main(inputs) { try { log.info(`\u{1F438} Running pullfrog/action@${package_default.version}...`); setupGitConfig(); - const { githubInstallationToken, wasAcquired } = await setupGitHubInstallationToken(); + const { githubInstallationToken, wasAcquired, isFallbackToken } = await setupGitHubInstallationToken(); if (wasAcquired) { tokenToRevoke = githubInstallationToken; } const repoContext = parseRepoContext(); - const repoSettings = await getRepoSettings(githubInstallationToken, repoContext); + let repoSettings; + if (isFallbackToken) { + log.info("Using default repository settings (app not installed)"); + repoSettings = DEFAULT_REPO_SETTINGS; + } else { + log.info("Fetching repository settings..."); + repoSettings = await getRepoSettings(githubInstallationToken, repoContext); + log.info("Repository settings fetched"); + } const agent = repoSettings.defaultAgent || "claude"; if (agent !== "claude") throw new Error(`Unsupported agent: ${agent}`); setupGitAuth(githubInstallationToken, repoContext); diff --git a/main.ts b/main.ts index 95d1861..d0184ef 100644 --- a/main.ts +++ b/main.ts @@ -2,7 +2,7 @@ import { type } from "arktype"; import { claude } from "./agents/claude.ts"; import { createMcpConfigs } from "./mcp/config.ts"; import packageJson from "./package.json" with { type: "json" }; -import { getRepoSettings } from "./utils/api.ts"; +import { DEFAULT_REPO_SETTINGS, getRepoSettings, type RepoSettings } from "./utils/api.ts"; import { log } from "./utils/cli.ts"; import { parseRepoContext, @@ -34,14 +34,24 @@ export async function main(inputs: Inputs): Promise { setupGitConfig(); - const { githubInstallationToken, wasAcquired } = await setupGitHubInstallationToken(); + const { githubInstallationToken, wasAcquired, isFallbackToken } = + await setupGitHubInstallationToken(); if (wasAcquired) { tokenToRevoke = githubInstallationToken; } const repoContext = parseRepoContext(); // Fetch repo settings (agent, permissions, workflows) from API - const repoSettings = await getRepoSettings(githubInstallationToken, repoContext); + // Skip API call if we're using GITHUB_TOKEN fallback (app not installed) + let repoSettings: RepoSettings; + if (isFallbackToken) { + log.info("Using default repository settings (app not installed)"); + repoSettings = DEFAULT_REPO_SETTINGS; + } else { + log.info("Fetching repository settings..."); + repoSettings = await getRepoSettings(githubInstallationToken, repoContext); + log.info("Repository settings fetched"); + } const agent = repoSettings.defaultAgent || "claude"; if (agent !== "claude") throw new Error(`Unsupported agent: ${agent}`); diff --git a/package.json b/package.json index 8c7c1fd..55ef10a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pullfrog/action", - "version": "0.0.95", + "version": "0.0.96", "type": "module", "files": [ "index.js", diff --git a/utils/api.ts b/utils/api.ts index f8c1981..617401c 100644 --- a/utils/api.ts +++ b/utils/api.ts @@ -13,7 +13,7 @@ export interface RepoSettings { }>; } -const DEFAULT_REPO_SETTINGS: RepoSettings = { +export const DEFAULT_REPO_SETTINGS: RepoSettings = { defaultAgent: null, webAccessLevel: "full_access", webAccessAllowTrusted: false, @@ -32,6 +32,11 @@ export async function getRepoSettings( ): Promise { const apiUrl = process.env.API_URL || "https://pullfrog.ai"; + // Add timeout to prevent hanging (5 seconds) + const timeoutMs = 5000; + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeoutMs); + try { const response = await fetch( `${apiUrl}/api/repo/${repoContext.owner}/${repoContext.name}/settings`, @@ -41,9 +46,12 @@ export async function getRepoSettings( Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, + signal: controller.signal, } ); + clearTimeout(timeoutId); + if (!response.ok) { // If API returns 404 or other error, fall back to defaults return DEFAULT_REPO_SETTINGS; @@ -58,7 +66,8 @@ export async function getRepoSettings( return settings; } catch { - // If fetch fails (network error, etc.), fall back to defaults + clearTimeout(timeoutId); + // If fetch fails (network error, timeout, etc.), fall back to defaults return DEFAULT_REPO_SETTINGS; } } diff --git a/utils/github.ts b/utils/github.ts index 10f5baa..22ddc9e 100644 --- a/utils/github.ts +++ b/utils/github.ts @@ -294,18 +294,19 @@ function getDefaultGitHubToken(): string | null { /** * Setup GitHub installation token for the action - * Returns the token and whether it was acquired (needs revocation) + * Returns the token, whether it was acquired (needs revocation), and whether we fell back to GITHUB_TOKEN * Falls back to GITHUB_TOKEN if app is not installed */ export async function setupGitHubInstallationToken(): Promise<{ githubInstallationToken: string; wasAcquired: boolean; + isFallbackToken: boolean; }> { const existingToken = checkExistingToken(); if (existingToken) { core.setSecret(existingToken); log.info("Using provided GitHub installation token"); - return { githubInstallationToken: existingToken, wasAcquired: false }; + return { githubInstallationToken: existingToken, wasAcquired: false, isFallbackToken: false }; } const acquiredToken = await acquireNewToken(); @@ -322,13 +323,13 @@ export async function setupGitHubInstallationToken(): Promise<{ log.info("Using GITHUB_TOKEN (app not installed or token exchange failed)"); core.setSecret(defaultToken); process.env.GITHUB_INSTALLATION_TOKEN = defaultToken; - return { githubInstallationToken: defaultToken, wasAcquired: false }; + return { githubInstallationToken: defaultToken, wasAcquired: false, isFallbackToken: true }; } core.setSecret(acquiredToken); process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken; - return { githubInstallationToken: acquiredToken, wasAcquired: true }; + return { githubInstallationToken: acquiredToken, wasAcquired: true, isFallbackToken: false }; } /**