Compare commits

...

4 Commits

Author SHA1 Message Date
David Blass 0e53a97619 improve github_token flow 2025-11-11 18:15:51 -05:00
David Blass cc56089a41 remove token input 2025-11-11 18:04:54 -05:00
David Blass 401496f19f read github token from inputs 2025-11-11 17:56:59 -05:00
David Blass 8822968cbb add debug logs 2025-11-11 17:45:29 -05:00
5 changed files with 90 additions and 26 deletions
+35 -11
View File
@@ -40481,7 +40481,7 @@ function query({
// package.json // package.json
var package_default = { var package_default = {
name: "@pullfrog/action", name: "@pullfrog/action",
version: "0.0.92", version: "0.0.96",
type: "module", type: "module",
files: [ files: [
"index.js", "index.js",
@@ -40911,6 +40911,9 @@ function checkExistingToken() {
const envToken = process.env.GITHUB_INSTALLATION_TOKEN; const envToken = process.env.GITHUB_INSTALLATION_TOKEN;
return inputToken || envToken || null; return inputToken || envToken || null;
} }
function getGitHubTokenInput() {
return core2.getInput("github_token") || null;
}
function isGitHubActionsEnvironment() { function isGitHubActionsEnvironment() {
return Boolean(process.env.GITHUB_ACTIONS); return Boolean(process.env.GITHUB_ACTIONS);
} }
@@ -41057,24 +41060,31 @@ async function acquireNewToken() {
} }
} }
function getDefaultGitHubToken() { function getDefaultGitHubToken() {
if (isGitHubActionsEnvironment()) { const inputToken = getGitHubTokenInput();
if (inputToken) {
return inputToken;
}
const token = process.env.GITHUB_TOKEN;
if (!token && isGitHubActionsEnvironment()) {
const githubEnvVars = Object.keys(process.env).filter((key) => key.startsWith("GITHUB_")).reduce( const githubEnvVars = Object.keys(process.env).filter((key) => key.startsWith("GITHUB_")).reduce(
(acc, key) => { (acc, key) => {
acc[key] = key === "GITHUB_TOKEN" ? "***" : process.env[key]; acc[key] = key === "GITHUB_TOKEN" ? process.env[key] ? "***SET***" : "NOT_SET" : process.env[key];
return acc; return acc;
}, },
{} {}
); );
log.debug(`GitHub env vars: ${JSON.stringify(githubEnvVars)}`); log.warning(
`GITHUB_TOKEN not found. Available GITHUB_* env vars: ${JSON.stringify(githubEnvVars)}`
);
} }
return process.env.GITHUB_TOKEN || null; return token || null;
} }
async function setupGitHubInstallationToken() { async function setupGitHubInstallationToken() {
const existingToken = checkExistingToken(); const existingToken = checkExistingToken();
if (existingToken) { if (existingToken) {
core2.setSecret(existingToken); core2.setSecret(existingToken);
log.info("Using provided GitHub installation token"); log.info("Using provided GitHub installation token");
return { githubInstallationToken: existingToken, wasAcquired: false }; return { githubInstallationToken: existingToken, wasAcquired: false, isFallbackToken: false };
} }
const acquiredToken = await acquireNewToken(); const acquiredToken = await acquireNewToken();
if (!acquiredToken) { if (!acquiredToken) {
@@ -41087,11 +41097,11 @@ async function setupGitHubInstallationToken() {
log.info("Using GITHUB_TOKEN (app not installed or token exchange failed)"); log.info("Using GITHUB_TOKEN (app not installed or token exchange failed)");
core2.setSecret(defaultToken); core2.setSecret(defaultToken);
process.env.GITHUB_INSTALLATION_TOKEN = defaultToken; process.env.GITHUB_INSTALLATION_TOKEN = defaultToken;
return { githubInstallationToken: defaultToken, wasAcquired: false }; return { githubInstallationToken: defaultToken, wasAcquired: false, isFallbackToken: true };
} }
core2.setSecret(acquiredToken); core2.setSecret(acquiredToken);
process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken; process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken;
return { githubInstallationToken: acquiredToken, wasAcquired: true }; return { githubInstallationToken: acquiredToken, wasAcquired: true, isFallbackToken: false };
} }
async function revokeInstallationToken(token) { async function revokeInstallationToken(token) {
const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com"; const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com";
@@ -41427,6 +41437,9 @@ var DEFAULT_REPO_SETTINGS = {
}; };
async function getRepoSettings(token, repoContext) { async function getRepoSettings(token, repoContext) {
const apiUrl = process.env.API_URL || "https://pullfrog.ai"; const apiUrl = process.env.API_URL || "https://pullfrog.ai";
const timeoutMs = 5e3;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try { try {
const response = await fetch( const response = await fetch(
`${apiUrl}/api/repo/${repoContext.owner}/${repoContext.name}/settings`, `${apiUrl}/api/repo/${repoContext.owner}/${repoContext.name}/settings`,
@@ -41435,9 +41448,11 @@ async function getRepoSettings(token, repoContext) {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
"Content-Type": "application/json" "Content-Type": "application/json"
} },
signal: controller.signal
} }
); );
clearTimeout(timeoutId);
if (!response.ok) { if (!response.ok) {
return DEFAULT_REPO_SETTINGS; return DEFAULT_REPO_SETTINGS;
} }
@@ -41447,6 +41462,7 @@ async function getRepoSettings(token, repoContext) {
} }
return settings; return settings;
} catch { } catch {
clearTimeout(timeoutId);
return DEFAULT_REPO_SETTINGS; return DEFAULT_REPO_SETTINGS;
} }
} }
@@ -41494,12 +41510,20 @@ async function main(inputs) {
try { try {
log.info(`\u{1F438} Running pullfrog/action@${package_default.version}...`); log.info(`\u{1F438} Running pullfrog/action@${package_default.version}...`);
setupGitConfig(); setupGitConfig();
const { githubInstallationToken, wasAcquired } = await setupGitHubInstallationToken(); const { githubInstallationToken, wasAcquired, isFallbackToken } = await setupGitHubInstallationToken();
if (wasAcquired) { if (wasAcquired) {
tokenToRevoke = githubInstallationToken; tokenToRevoke = githubInstallationToken;
} }
const repoContext = parseRepoContext(); 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"; const agent = repoSettings.defaultAgent || "claude";
if (agent !== "claude") throw new Error(`Unsupported agent: ${agent}`); if (agent !== "claude") throw new Error(`Unsupported agent: ${agent}`);
setupGitAuth(githubInstallationToken, repoContext); setupGitAuth(githubInstallationToken, repoContext);
+13 -3
View File
@@ -2,7 +2,7 @@ import { type } from "arktype";
import { claude } from "./agents/claude.ts"; import { claude } from "./agents/claude.ts";
import { createMcpConfigs } from "./mcp/config.ts"; import { createMcpConfigs } from "./mcp/config.ts";
import packageJson from "./package.json" with { type: "json" }; 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 { log } from "./utils/cli.ts";
import { import {
parseRepoContext, parseRepoContext,
@@ -34,14 +34,24 @@ export async function main(inputs: Inputs): Promise<MainResult> {
setupGitConfig(); setupGitConfig();
const { githubInstallationToken, wasAcquired } = await setupGitHubInstallationToken(); const { githubInstallationToken, wasAcquired, isFallbackToken } =
await setupGitHubInstallationToken();
if (wasAcquired) { if (wasAcquired) {
tokenToRevoke = githubInstallationToken; tokenToRevoke = githubInstallationToken;
} }
const repoContext = parseRepoContext(); const repoContext = parseRepoContext();
// Fetch repo settings (agent, permissions, workflows) from API // 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"; const agent = repoSettings.defaultAgent || "claude";
if (agent !== "claude") throw new Error(`Unsupported agent: ${agent}`); if (agent !== "claude") throw new Error(`Unsupported agent: ${agent}`);
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@pullfrog/action", "name": "@pullfrog/action",
"version": "0.0.92", "version": "0.0.96",
"type": "module", "type": "module",
"files": [ "files": [
"index.js", "index.js",
+11 -2
View File
@@ -13,7 +13,7 @@ export interface RepoSettings {
}>; }>;
} }
const DEFAULT_REPO_SETTINGS: RepoSettings = { export const DEFAULT_REPO_SETTINGS: RepoSettings = {
defaultAgent: null, defaultAgent: null,
webAccessLevel: "full_access", webAccessLevel: "full_access",
webAccessAllowTrusted: false, webAccessAllowTrusted: false,
@@ -32,6 +32,11 @@ export async function getRepoSettings(
): Promise<RepoSettings> { ): Promise<RepoSettings> {
const apiUrl = process.env.API_URL || "https://pullfrog.ai"; 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 { try {
const response = await fetch( const response = await fetch(
`${apiUrl}/api/repo/${repoContext.owner}/${repoContext.name}/settings`, `${apiUrl}/api/repo/${repoContext.owner}/${repoContext.name}/settings`,
@@ -41,9 +46,12 @@ export async function getRepoSettings(
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
signal: controller.signal,
} }
); );
clearTimeout(timeoutId);
if (!response.ok) { if (!response.ok) {
// If API returns 404 or other error, fall back to defaults // If API returns 404 or other error, fall back to defaults
return DEFAULT_REPO_SETTINGS; return DEFAULT_REPO_SETTINGS;
@@ -58,7 +66,8 @@ export async function getRepoSettings(
return settings; return settings;
} catch { } 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; return DEFAULT_REPO_SETTINGS;
} }
} }
+30 -9
View File
@@ -49,6 +49,10 @@ function checkExistingToken(): string | null {
return inputToken || envToken || null; return inputToken || envToken || null;
} }
function getGitHubTokenInput(): string | null {
return core.getInput("github_token") || null;
}
function isGitHubActionsEnvironment(): boolean { function isGitHubActionsEnvironment(): boolean {
return Boolean(process.env.GITHUB_ACTIONS); return Boolean(process.env.GITHUB_ACTIONS);
} }
@@ -255,37 +259,54 @@ async function acquireNewToken(): Promise<string | null> {
} }
function getDefaultGitHubToken(): string | null { function getDefaultGitHubToken(): string | null {
// Debug: log all GITHUB_* env vars // Try input first (explicitly passed from workflow)
if (isGitHubActionsEnvironment()) { const inputToken = getGitHubTokenInput();
if (inputToken) {
return inputToken;
}
// Then try environment variable (automatically set by GitHub Actions)
const token = process.env.GITHUB_TOKEN;
// Log diagnostic info when GITHUB_TOKEN is missing
if (!token && isGitHubActionsEnvironment()) {
const githubEnvVars = Object.keys(process.env) const githubEnvVars = Object.keys(process.env)
.filter((key) => key.startsWith("GITHUB_")) .filter((key) => key.startsWith("GITHUB_"))
.reduce( .reduce(
(acc, key) => { (acc, key) => {
acc[key] = key === "GITHUB_TOKEN" ? "***" : process.env[key]; acc[key] =
key === "GITHUB_TOKEN"
? process.env[key]
? "***SET***"
: "NOT_SET"
: process.env[key];
return acc; return acc;
}, },
{} as Record<string, string | undefined> {} as Record<string, string | undefined>
); );
log.debug(`GitHub env vars: ${JSON.stringify(githubEnvVars)}`); log.warning(
`GITHUB_TOKEN not found. Available GITHUB_* env vars: ${JSON.stringify(githubEnvVars)}`
);
} }
return process.env.GITHUB_TOKEN || null; return token || null;
} }
/** /**
* Setup GitHub installation token for the action * 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 * Falls back to GITHUB_TOKEN if app is not installed
*/ */
export async function setupGitHubInstallationToken(): Promise<{ export async function setupGitHubInstallationToken(): Promise<{
githubInstallationToken: string; githubInstallationToken: string;
wasAcquired: boolean; wasAcquired: boolean;
isFallbackToken: boolean;
}> { }> {
const existingToken = checkExistingToken(); const existingToken = checkExistingToken();
if (existingToken) { if (existingToken) {
core.setSecret(existingToken); core.setSecret(existingToken);
log.info("Using provided GitHub installation token"); log.info("Using provided GitHub installation token");
return { githubInstallationToken: existingToken, wasAcquired: false }; return { githubInstallationToken: existingToken, wasAcquired: false, isFallbackToken: false };
} }
const acquiredToken = await acquireNewToken(); const acquiredToken = await acquireNewToken();
@@ -302,13 +323,13 @@ export async function setupGitHubInstallationToken(): Promise<{
log.info("Using GITHUB_TOKEN (app not installed or token exchange failed)"); log.info("Using GITHUB_TOKEN (app not installed or token exchange failed)");
core.setSecret(defaultToken); core.setSecret(defaultToken);
process.env.GITHUB_INSTALLATION_TOKEN = defaultToken; process.env.GITHUB_INSTALLATION_TOKEN = defaultToken;
return { githubInstallationToken: defaultToken, wasAcquired: false }; return { githubInstallationToken: defaultToken, wasAcquired: false, isFallbackToken: true };
} }
core.setSecret(acquiredToken); core.setSecret(acquiredToken);
process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken; process.env.GITHUB_INSTALLATION_TOKEN = acquiredToken;
return { githubInstallationToken: acquiredToken, wasAcquired: true }; return { githubInstallationToken: acquiredToken, wasAcquired: true, isFallbackToken: false };
} }
/** /**