improve logging, remove act

This commit is contained in:
David Blass
2025-10-31 00:25:02 -04:00
parent b2badf6d16
commit 876663cd1a
8 changed files with 375 additions and 158 deletions
-73
View File
@@ -1,73 +0,0 @@
import { execSync } from "node:child_process";
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { config } from "dotenv";
import { setupTestRepo } from "./setup.ts";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const tempDir = join(__dirname, "..", ".temp");
const actionPath = join(__dirname, "..");
const envPath = join(__dirname, "..", "..", ".env");
const ENV_VARS = ["ANTHROPIC_API_KEY", "GITHUB_INSTALLATION_TOKEN"];
export function runAct(prompt: string): void {
setupTestRepo({ tempDir });
config({ path: envPath });
const workflowPath = join(tempDir, ".github", "workflows", "pullfrog.yml");
const distPath = join(actionPath, ".act-dist");
console.log("📦 Creating minimal distribution for act...");
execSync(`rm -rf "${distPath}" && mkdir -p "${distPath}"`, { shell: "/bin/bash" });
["action.yml", "entry.cjs", "index.cjs", "package.json"].forEach((file) => {
const src = join(actionPath, file);
if (existsSync(src)) {
execSync(`cp "${src}" "${distPath}"`);
}
});
try {
const escapedPrompt = prompt.replace(/'/g, "'\\''");
const actCommandParts = [
"act",
"workflow_dispatch",
"-W",
workflowPath,
"--input",
`prompt='${escapedPrompt}'`,
"--local-repository",
`pullfrog/action@v0=${distPath}`, // Use minimal dist without symlinks
];
ENV_VARS.forEach((key) => {
if (process.env[key]) {
actCommandParts.push("-s", key);
}
});
const actCommand = actCommandParts.join(" ");
console.log("🚀 Running act with prompt:");
console.log("─".repeat(50));
console.log(prompt);
console.log("─".repeat(50));
console.log("");
execSync(actCommand, {
stdio: "inherit",
cwd: join(__dirname, "..", ".."),
});
execSync(`rm -rf "${distPath}"`);
} catch (error) {
execSync(`rm -rf "${distPath}"`);
console.error("❌ Act execution failed:", (error as Error).message);
process.exit(1);
}
}
+315
View File
@@ -0,0 +1,315 @@
/**
* CLI output utilities for GitHub Actions and local development
* Uses GitHub Actions Job Summaries API when available for rich formatting
*/
import * as core from "@actions/core";
const isGitHubActions = !!process.env.GITHUB_ACTIONS;
/**
* Print a formatted box with text (for console output)
*/
function boxString(
text: string,
options?: {
title?: string;
maxWidth?: number;
indent?: string;
padding?: number;
}
): string {
const { title, maxWidth = 80, indent = "", padding = 1 } = options || {};
const lines = text.trim().split("\n");
const wrappedLines: string[] = [];
for (const line of lines) {
if (line.length <= maxWidth - padding * 2) {
wrappedLines.push(line);
} else {
const words = line.split(" ");
let currentLine = "";
for (const word of words) {
const testLine = currentLine ? `${currentLine} ${word}` : word;
if (testLine.length <= maxWidth - padding * 2) {
currentLine = testLine;
} else {
if (currentLine) {
wrappedLines.push(currentLine);
currentLine = word;
} else {
wrappedLines.push(word.substring(0, maxWidth - padding * 2));
currentLine = word.substring(maxWidth - padding * 2);
}
}
}
if (currentLine) {
wrappedLines.push(currentLine);
}
}
}
const maxLineLength = Math.max(...wrappedLines.map((line) => line.length));
const boxWidth = maxLineLength + padding * 2;
let result = "";
if (title) {
const titleLine = ` ${title} `;
const titlePadding = Math.max(0, boxWidth - titleLine.length);
result += `${indent}${titleLine}${"─".repeat(titlePadding)}\n`;
}
if (!title) {
result += `${indent}${"─".repeat(boxWidth)}\n`;
}
for (const line of wrappedLines) {
const paddedLine = line.padEnd(maxLineLength);
result += `${indent}${" ".repeat(padding)}${paddedLine}${" ".repeat(padding)}\n`;
}
result += `${indent}${"─".repeat(boxWidth)}`;
return result;
}
/**
* Print a formatted box with text
* In GitHub Actions, uses markdown code blocks; locally uses box formatting
*/
function box(
text: string,
options?: {
title?: string;
maxWidth?: number;
}
): void {
if (isGitHubActions) {
const { title } = options || {};
let markdown = "";
if (title) {
markdown += `### ${title}\n\n`;
}
markdown += "```\n";
markdown += text;
markdown += "\n```\n";
// Note: summary.write() should be called once at the end, but for immediate visibility we call it here
// In practice, you might want to batch multiple calls
core.summary.addRaw(markdown);
core.info(markdown);
} else {
log.info(boxString(text, options));
}
}
/**
* Add a table using GitHub Actions Job Summaries API
* Falls back to formatted console table locally
*/
async function table(
rows: Array<Array<{ data: string; header?: boolean } | string>>,
options?: {
title?: string;
}
): Promise<void> {
const { title } = options || {};
if (isGitHubActions) {
const summary = core.summary;
if (title) {
summary.addHeading(title);
}
// Convert rows to format expected by Job Summaries API
const formattedRows = rows.map((row) =>
row.map((cell) => {
if (typeof cell === "string") {
return { data: cell };
}
return cell;
})
);
summary.addTable(formattedRows);
await summary.write();
// Also log to console for visibility in logs
const tableText = formattedRows
.map((row) => row.map((cell) => cell.data).join(" | "))
.join("\n");
log.info(tableText);
} else {
// Local fallback: simple console table
if (title) {
log.info(`\n${title}`);
}
const tableText = rows
.map((row) => {
const rowData = row.map((cell) => {
const text = typeof cell === "string" ? cell : cell.data;
const isHeader = typeof cell !== "string" && cell.header;
return isHeader ? `**${text}**` : text;
});
return rowData.join(" | ");
})
.join("\n");
log.info(`\n${tableText}\n`);
}
}
/**
* Add raw markdown to job summary (GitHub Actions) or print as info (local)
*/
async function markdown(content: string): Promise<void> {
if (isGitHubActions) {
core.summary.addRaw(content);
await core.summary.write();
// Also log to console
core.info(content);
} else {
log.info(content);
}
}
/**
* Add a code block to output
*/
function codeBlock(code: string, language?: string): void {
if (isGitHubActions) {
core.summary.addCodeBlock(code, language);
core.info(`\`\`\`${language || ""}\n${code}\n\`\`\``);
} else {
log.info(`\`\`\`${language || ""}\n${code}\n\`\`\``);
}
}
/**
* Add a link to output
*/
function link(text: string, url: string): void {
if (isGitHubActions) {
core.summary.addLink(text, url);
log.info(`[${text}](${url})`);
} else {
log.info(`[${text}](${url})`);
}
}
/**
* Write all pending summary changes to the job summary
* Call this at the end of your action to ensure all summary content is written
*/
async function writeSummary(): Promise<void> {
if (isGitHubActions) {
await core.summary.write();
}
}
/**
* Print a separator line
*/
function separator(length: number = 50): void {
if (isGitHubActions) {
core.info("─".repeat(length));
} else {
console.log("─".repeat(length));
}
}
/**
* Main logging utility object - import this once and access all utilities
*/
export const log = {
/**
* Check if running in GitHub Actions environment
*/
isGitHubActions: (): boolean => isGitHubActions,
/**
* Print info message (GitHub Actions or console)
*/
info: (message: string): void => {
if (isGitHubActions) {
core.info(message);
} else {
console.log(message);
}
},
/**
* Print warning message
*/
warning: (message: string): void => {
if (isGitHubActions) {
core.warning(message);
} else {
console.warn(`⚠️ ${message}`);
}
},
/**
* Print error message
*/
error: (message: string): void => {
if (isGitHubActions) {
core.error(message);
} else {
console.error(`${message}`);
}
},
/**
* Print success message
*/
success: (message: string): void => {
const msg = `${message}`;
if (isGitHubActions) {
core.info(msg);
} else {
console.log(msg);
}
},
/**
* Print a formatted box with text
* In GitHub Actions, uses markdown code blocks; locally uses box formatting
*/
box,
/**
* Add a table using GitHub Actions Job Summaries API
* Falls back to formatted console table locally
*/
table,
/**
* Add raw markdown to job summary (GitHub Actions) or print as info (local)
*/
markdown,
/**
* Add a code block to output
*/
codeBlock,
/**
* Add a link to output
*/
link,
/**
* Write all pending summary changes to the job summary
* Call this at the end of your action to ensure all summary content is written
*/
writeSummary,
/**
* Print a separator line
*/
separator,
};