Fix CI
This commit is contained in:
@@ -4,10 +4,3 @@ if git diff --cached --name-only | grep -q "^package.json$"; then
|
|||||||
pnpm lock
|
pnpm lock
|
||||||
git add pnpm-lock.yaml
|
git add pnpm-lock.yaml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if entry needs rebuilding (entry.ts, esbuild.config.js, or any .ts files)
|
|
||||||
if git diff --cached --name-only | grep -qE "^(entry\.ts|esbuild\.config\.js|.*\.ts)$"; then
|
|
||||||
echo "🔨 Building action..."
|
|
||||||
pnpm build
|
|
||||||
git add entry
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
|
|
||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
<picture>
|
<picture>
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://pullfrog.com/frog-white-200px.png">
|
<source media="(prefers-color-scheme: dark)" srcset="https://pullfrog.com/frog-white-200px.png">
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ inputs:
|
|||||||
description: "Effort level: nothink (fast), think (default), max (most capable)"
|
description: "Effort level: nothink (fast), think (default), max (most capable)"
|
||||||
required: false
|
required: false
|
||||||
default: "think"
|
default: "think"
|
||||||
|
cwd:
|
||||||
|
description: "Working directory for the agent (defaults to GITHUB_WORKSPACE)"
|
||||||
|
required: false
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "node24"
|
using: "node24"
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import { Inputs, main } from "./main.ts";
|
|||||||
import { log } from "./utils/cli.ts";
|
import { log } from "./utils/cli.ts";
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
// Change to GITHUB_WORKSPACE if set (this is where actions/checkout puts the repo)
|
// Change to cwd input or GITHUB_WORKSPACE (where actions/checkout puts the repo)
|
||||||
// JavaScript actions run from the action's directory, not the checked out repo
|
// JavaScript actions run from the action's directory, not the checked out repo
|
||||||
if (process.env.GITHUB_WORKSPACE && process.cwd() !== process.env.GITHUB_WORKSPACE) {
|
const cwd = core.getInput("cwd") || process.env.GITHUB_WORKSPACE;
|
||||||
log.debug(`Changing to GITHUB_WORKSPACE: ${process.env.GITHUB_WORKSPACE}`);
|
if (cwd && process.cwd() !== cwd) {
|
||||||
process.chdir(process.env.GITHUB_WORKSPACE);
|
log.debug(`changing to working directory: ${cwd}`);
|
||||||
log.debug(`New working directory: ${process.cwd()}`);
|
process.chdir(cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -67,4 +67,12 @@ await build({
|
|||||||
plugins: [stripShebangPlugin],
|
plugins: [stripShebangPlugin],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Build the get-installation-token action
|
||||||
|
await build({
|
||||||
|
...sharedConfig,
|
||||||
|
entryPoints: ["./get-installation-token/entry.ts"],
|
||||||
|
outfile: "./get-installation-token/entry",
|
||||||
|
plugins: [stripShebangPlugin],
|
||||||
|
});
|
||||||
|
|
||||||
console.log("✅ Build completed successfully!");
|
console.log("✅ Build completed successfully!");
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: "Get Installation Token"
|
||||||
|
description: "Get a GitHub App installation token for the current repository"
|
||||||
|
author: "Pullfrog"
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
repos:
|
||||||
|
description: "Comma-separated list of additional repo names to grant access to (e.g., 'repo1,repo2'). Current repo is always included."
|
||||||
|
required: false
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
token:
|
||||||
|
description: "GitHub App installation token"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "node24"
|
||||||
|
main: "entry"
|
||||||
|
post: "entry"
|
||||||
|
|
||||||
|
branding:
|
||||||
|
icon: "key"
|
||||||
|
color: "green"
|
||||||
Executable
+26030
File diff suppressed because one or more lines are too long
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* entry point for get-installation-token action.
|
||||||
|
* handles both main and post execution using the isPost state pattern.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import { acquireInstallationToken, revokeInstallationToken } from "./token.ts";
|
||||||
|
|
||||||
|
const STATE_TOKEN = "token";
|
||||||
|
const STATE_IS_POST = "isPost";
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
core.saveState(STATE_IS_POST, "true");
|
||||||
|
|
||||||
|
const reposInput = core.getInput("repos");
|
||||||
|
const repos = reposInput
|
||||||
|
? reposInput.split(",").map((r) => r.trim()).filter(Boolean)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const token = await acquireInstallationToken({ repos });
|
||||||
|
|
||||||
|
// mask the token in logs
|
||||||
|
core.setSecret(token);
|
||||||
|
|
||||||
|
// save token to state for post cleanup
|
||||||
|
core.saveState(STATE_TOKEN, token);
|
||||||
|
|
||||||
|
// set as output
|
||||||
|
core.setOutput("token", token);
|
||||||
|
|
||||||
|
const scope = repos?.length
|
||||||
|
? `current repo + ${repos.join(", ")}`
|
||||||
|
: "current repo only";
|
||||||
|
core.info(`installation token acquired successfully (${scope})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function post(): Promise<void> {
|
||||||
|
const token = core.getState(STATE_TOKEN);
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
core.debug("no token found in state, skipping revocation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await revokeInstallationToken(token);
|
||||||
|
core.info("installation token revoked successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const isPost = core.getState(STATE_IS_POST) === "true";
|
||||||
|
|
||||||
|
if (isPost) {
|
||||||
|
await post();
|
||||||
|
} else {
|
||||||
|
await main();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
core.setFailed(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await run();
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* token acquisition and revocation for get-installation-token action.
|
||||||
|
* reuses the existing github.ts utilities.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { acquireNewToken, revokeGitHubInstallationToken } from "../utils/github.ts";
|
||||||
|
|
||||||
|
export async function acquireInstallationToken(opts?: { repos?: string[] }): Promise<string> {
|
||||||
|
return acquireNewToken(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function revokeInstallationToken(token: string): Promise<void> {
|
||||||
|
return revokeGitHubInstallationToken(token);
|
||||||
|
}
|
||||||
+15
-6
@@ -54,12 +54,17 @@ function isOIDCAvailable(): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function acquireTokenViaOIDC(): Promise<string> {
|
async function acquireTokenViaOIDC(opts?: { repos?: string[] }): Promise<string> {
|
||||||
log.info("» generating OIDC token...");
|
log.info("» generating OIDC token...");
|
||||||
|
|
||||||
const oidcToken = await core.getIDToken("pullfrog-api");
|
const oidcToken = await core.getIDToken("pullfrog-api");
|
||||||
|
|
||||||
const apiUrl = process.env.API_URL || "https://pullfrog.com";
|
const apiUrl = process.env.API_URL || "https://pullfrog.com";
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (opts?.repos?.length) {
|
||||||
|
params.set("repos", opts.repos.join(","));
|
||||||
|
}
|
||||||
|
const queryString = params.toString() ? `?${params.toString()}` : "";
|
||||||
|
|
||||||
log.info("» exchanging OIDC token for installation token...");
|
log.info("» exchanging OIDC token for installation token...");
|
||||||
|
|
||||||
@@ -68,7 +73,7 @@ async function acquireTokenViaOIDC(): Promise<string> {
|
|||||||
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tokenResponse = await fetch(`${apiUrl}/api/github/installation-token`, {
|
const tokenResponse = await fetch(`${apiUrl}/api/github/installation-token${queryString}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${oidcToken}`,
|
Authorization: `Bearer ${oidcToken}`,
|
||||||
@@ -84,7 +89,11 @@ async function acquireTokenViaOIDC(): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tokenData = (await tokenResponse.json()) as InstallationToken;
|
const tokenData = (await tokenResponse.json()) as InstallationToken;
|
||||||
log.info(`» installation token obtained for ${tokenData.repository || "all repositories"}`);
|
const owner = tokenData.repository?.split("/")[0];
|
||||||
|
const repoList = opts?.repos?.length
|
||||||
|
? [tokenData.repository, ...opts.repos.map((r) => `${owner}/${r}`)].join(", ")
|
||||||
|
: tokenData.repository;
|
||||||
|
log.info(`» installation token obtained for ${repoList}`);
|
||||||
|
|
||||||
return tokenData.token;
|
return tokenData.token;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -238,9 +247,9 @@ async function acquireTokenViaGitHubApp(): Promise<string> {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function acquireNewToken(): Promise<string> {
|
export async function acquireNewToken(opts?: { repos?: string[] }): Promise<string> {
|
||||||
if (isOIDCAvailable()) {
|
if (isOIDCAvailable()) {
|
||||||
return await retry(() => acquireTokenViaOIDC(), { label: "token exchange" });
|
return await retry(() => acquireTokenViaOIDC(opts), { label: "token exchange" });
|
||||||
} else {
|
} else {
|
||||||
return await acquireTokenViaGitHubApp();
|
return await acquireTokenViaGitHubApp();
|
||||||
}
|
}
|
||||||
@@ -277,7 +286,7 @@ export function getGitHubInstallationToken(): string {
|
|||||||
return githubInstallationToken;
|
return githubInstallationToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function revokeGitHubInstallationToken(token: string): Promise<void> {
|
export async function revokeGitHubInstallationToken(token: string): Promise<void> {
|
||||||
const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com";
|
const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user