feat: integrate OIDC token exchange in GitHub Action

- Add setupGitHubInstallationToken utility for OIDC token generation
- Implement automatic token exchange with Pullfrog API endpoint
- Add support for multiple authentication methods (input, env, OIDC)
- Create setup utilities for test repository management
- Update action entry point to handle new token flow
- Add environment variable documentation for API key
- Remove large bundled dependencies and optimize build
- Support both development and production token workflows
This commit is contained in:
Colin McDonnell
2025-09-10 00:30:45 -07:00
parent c5b9c7cfc4
commit 3139f541e4
14 changed files with 719 additions and 26533 deletions
+22 -41
View File
@@ -1,59 +1,36 @@
import { execSync } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { config, parse } from "dotenv";
import { config } from "dotenv";
import { buildAction, setupTestRepo } from "./setup";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Environment variables that should be passed as secrets to the workflow
const ENV_VARS = ["ANTHROPIC_API_KEY", "GITHUB_INSTALLATION_TOKEN"];
export function runAct(prompt: string): void {
// First, ensure the scratch repo is cloned
const tempDir = join(__dirname, "..", ".temp");
// Check if .temp exists and either reset it or clone it
if (existsSync(tempDir)) {
console.log("📦 Resetting existing .temp repository...");
execSync("git reset --hard HEAD && git clean -fd", {
cwd: tempDir,
stdio: "inherit",
});
} else {
console.log("📦 Cloning pullfrogai/scratch into .temp...");
const repoUrl = "git@github.com:pullfrogai/scratch.git";
execSync(`git clone ${repoUrl} ${tempDir}`, { stdio: "inherit" });
}
const workflowPath = join(tempDir, ".github", "workflows", "pullfrog.yml");
const actionPath = join(__dirname, "..");
const envPath = join(__dirname, "..", "..", ".env");
// Load environment variables into process
// Setup test repository
setupTestRepo({ tempDir });
// Load environment variables
config({ path: envPath });
// Parse environment variables from .env file to get keys
let envVars: string[] = [];
try {
const content = readFileSync(envPath, "utf8");
const parsed = parse(content);
envVars = Object.keys(parsed);
} catch (error) {
console.warn(
`Warning: Could not read .env file: ${(error as Error).message}`,
);
}
// Build action bundles
buildAction(actionPath);
// Build fresh bundles with esbuild
const actionPath = join(__dirname, "..");
console.log("🔨 Building fresh bundles with esbuild...");
execSync("node esbuild.config.js", {
cwd: actionPath,
stdio: "inherit",
});
const workflowPath = join(tempDir, ".github", "workflows", "pullfrog.yml");
// Create minimal dist for act (avoids pnpm symlink issues)
const distPath = join(actionPath, ".act-dist");
console.log("📦 Creating minimal distribution for act...");
execSync(`rm -rf "${distPath}" && mkdir -p "${distPath}"`, { shell: true });
execSync(`rm -rf "${distPath}" && mkdir -p "${distPath}"`, { shell: "/bin/bash" });
// Copy only necessary files (bundled, no node_modules needed)
["action.yml", "entry.cjs", "index.cjs", "package.json"].forEach((file) => {
@@ -79,11 +56,15 @@ export function runAct(prompt: string): void {
`pullfrog/action@v0=${distPath}`, // Use minimal dist without symlinks
];
// Add all environment variables as secrets (without values)
envVars.forEach((key) => {
actCommandParts.push("-s", key);
// Add environment variables as secrets that will be available to the workflow
ENV_VARS.forEach((key) => {
if (process.env[key]) {
actCommandParts.push("-s", key);
}
});
// We only need the specific ENV_VARS, no need to add other variables
const actCommand = actCommandParts.join(" ");
console.log("🚀 Running act with prompt:");