revoke installation token after action run

This commit is contained in:
David Blass
2025-11-11 17:08:20 -05:00
parent aaba4b7650
commit a3f1593e28
5 changed files with 95 additions and 22 deletions
+32 -6
View File
@@ -41044,12 +41044,30 @@ async function setupGitHubInstallationToken() {
if (existingToken) {
core2.setSecret(existingToken);
log.info("Using provided GitHub installation token");
return existingToken;
return { githubInstallationToken: existingToken, wasAcquired: false };
}
const githubInstallationToken = await acquireNewToken();
core2.setSecret(githubInstallationToken);
process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken;
return { githubInstallationToken, wasAcquired: true };
}
async function revokeInstallationToken(token) {
const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com";
try {
await fetch(`${apiUrl}/installation/token`, {
method: "DELETE",
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${token}`,
"X-GitHub-Api-Version": "2022-11-28"
}
});
log.info("Installation token revoked");
} catch (error2) {
log.warning(
`Failed to revoke installation token: ${error2 instanceof Error ? error2.message : String(error2)}`
);
}
const token = await acquireNewToken();
core2.setSecret(token);
process.env.GITHUB_INSTALLATION_TOKEN = token;
return token;
}
function parseRepoContext() {
const githubRepo = process.env.GITHUB_REPOSITORY;
@@ -41430,10 +41448,14 @@ var Inputs = type({
"anthropic_api_key?": "string | undefined"
});
async function main(inputs) {
let tokenToRevoke = null;
try {
log.info(`\u{1F438} Running pullfrog/action@${package_default.version}...`);
setupGitConfig();
const githubInstallationToken = await setupGitHubInstallationToken();
const { githubInstallationToken, wasAcquired } = await setupGitHubInstallationToken();
if (wasAcquired) {
tokenToRevoke = githubInstallationToken;
}
const repoContext = parseRepoContext();
const repoSettings = await getRepoSettings(githubInstallationToken, repoContext);
const agent = repoSettings.defaultAgent || "claude";
@@ -41471,6 +41493,10 @@ async function main(inputs) {
success: false,
error: errorMessage
};
} finally {
if (tokenToRevoke) {
await revokeInstallationToken(tokenToRevoke);
}
}
}
+1 -1
View File
@@ -1 +1 @@
create a JSON file with the current process.env
ribbit like a froggy
+15 -2
View File
@@ -4,7 +4,11 @@ import { createMcpConfigs } from "./mcp/config.ts";
import packageJson from "./package.json" with { type: "json" };
import { getRepoSettings } from "./utils/api.ts";
import { log } from "./utils/cli.ts";
import { parseRepoContext, setupGitHubInstallationToken } from "./utils/github.ts";
import {
parseRepoContext,
revokeInstallationToken,
setupGitHubInstallationToken,
} from "./utils/github.ts";
import { setupGitAuth, setupGitConfig } from "./utils/setup.ts";
export const Inputs = type({
@@ -23,12 +27,17 @@ export interface MainResult {
export type PromptJSON = {};
export async function main(inputs: Inputs): Promise<MainResult> {
let tokenToRevoke: string | null = null;
try {
log.info(`🐸 Running pullfrog/action@${packageJson.version}...`);
setupGitConfig();
const githubInstallationToken = await setupGitHubInstallationToken();
const { githubInstallationToken, wasAcquired } = await setupGitHubInstallationToken();
if (wasAcquired) {
tokenToRevoke = githubInstallationToken;
}
const repoContext = parseRepoContext();
// Fetch repo settings (agent, permissions, workflows) from API
@@ -82,5 +91,9 @@ export async function main(inputs: Inputs): Promise<MainResult> {
success: false,
error: errorMessage,
};
} finally {
if (tokenToRevoke) {
await revokeInstallationToken(tokenToRevoke);
}
}
}
+14 -7
View File
@@ -1,13 +1,20 @@
## CURRENT
[] look into trigger.yml without installation
[] try to find heavy claude code user
[] investigate including terminal output from bash commands as collapsed groups
[] test initialization trade offs for pullfrog.yml
## MAYBE
[] investigate repo config file
## DONE
[x] add modes to prompt
[x] progressively update comment
[x] don't allow rejecting prs
[x] fix pnpm caching
[x] fix prompt to avoid narration like "I just read all tools from MCP server"
[x] avoid exposing env adding ## SECURITY prompt
[] investigate including terminal output from bash commands as collapsed groups
[] test initialization trade offs for pullfrog.yml
[] try to find heavy claude code user
[] investigate repo config file?
[] look into trigger.yml without installation
[] cancel installation token at the end of github aciton
[x] cancel installation token at the end of github aciton
+33 -6
View File
@@ -234,21 +234,48 @@ async function acquireNewToken(): Promise<string> {
/**
* Setup GitHub installation token for the action
* Returns the token and whether it was acquired (needs revocation)
*/
export async function setupGitHubInstallationToken(): Promise<string> {
export async function setupGitHubInstallationToken(): Promise<{
githubInstallationToken: string;
wasAcquired: boolean;
}> {
const existingToken = checkExistingToken();
if (existingToken) {
core.setSecret(existingToken);
log.info("Using provided GitHub installation token");
return existingToken;
return { githubInstallationToken: existingToken, wasAcquired: false };
}
const token = await acquireNewToken();
const githubInstallationToken = await acquireNewToken();
core.setSecret(token);
process.env.GITHUB_INSTALLATION_TOKEN = token;
core.setSecret(githubInstallationToken);
process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken;
return token;
return { githubInstallationToken, wasAcquired: true };
}
/**
* Revoke GitHub installation token
*/
export async function revokeInstallationToken(token: string): Promise<void> {
const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com";
try {
await fetch(`${apiUrl}/installation/token`, {
method: "DELETE",
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${token}`,
"X-GitHub-Api-Version": "2022-11-28",
},
});
log.info("Installation token revoked");
} catch (error) {
log.warning(
`Failed to revoke installation token: ${error instanceof Error ? error.message : String(error)}`
);
}
}
export interface RepoContext {