diff --git a/.changeset/patch-refactor-mcp-server-utilities.md b/.changeset/patch-refactor-mcp-server-utilities.md new file mode 100644 index 00000000000..1ae877bee38 --- /dev/null +++ b/.changeset/patch-refactor-mcp-server-utilities.md @@ -0,0 +1,7 @@ +--- +"gh-aw": patch +--- + +Refactor safe_outputs_mcp_server.cjs: Extract utility functions with bundling and size logging + +Extracted 7 utility functions from the monolithic safe_outputs_mcp_server.cjs file into separate, well-tested modules with automatic bundling. Added 50 comprehensive unit tests, detailed size logging during bundling, and fixed MCP server script generation bug. All functionality preserved with no breaking changes. diff --git a/.github/workflows/ai-triage-campaign.lock.yml b/.github/workflows/ai-triage-campaign.lock.yml index f92613a8f85..c2ebd4902e0 100644 --- a/.github/workflows/ai-triage-campaign.lock.yml +++ b/.github/workflows/ai-triage-campaign.lock.yml @@ -519,9 +519,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -536,6 +533,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -636,51 +792,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -800,156 +911,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index c0682c378a5..27383711776 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -1532,9 +1532,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1549,6 +1546,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1649,51 +1805,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1813,156 +1924,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index e5a943001a2..34bda1e9536 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -453,9 +453,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -470,6 +467,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -570,51 +726,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -734,156 +845,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 3107833629e..a2acda06079 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -1214,9 +1214,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1231,6 +1228,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1331,51 +1487,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1495,156 +1606,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index 388f4152e2d..601a054e83b 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -735,9 +735,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -752,6 +749,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -852,51 +1008,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1016,156 +1127,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 4913e6c5db6..ca15cf73457 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1424,9 +1424,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1441,6 +1438,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1541,51 +1697,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1705,156 +1816,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index a68de360534..f97bfae82b6 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -1168,9 +1168,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1185,6 +1182,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1285,51 +1441,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1449,156 +1560,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 9f49955bbdc..6c9d8fdad4b 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -899,9 +899,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -916,6 +913,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1016,51 +1172,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1180,156 +1291,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml index bbdcf3871a7..baa1bffdd0c 100644 --- a/.github/workflows/cli-consistency-checker.lock.yml +++ b/.github/workflows/cli-consistency-checker.lock.yml @@ -464,9 +464,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -481,6 +478,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -581,51 +737,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -745,156 +856,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 69b75f87a75..12b10d0cb8d 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -629,9 +629,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -646,6 +643,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -746,51 +902,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -910,156 +1021,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index 2b1f563d4e0..8a97d4596fa 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1773,9 +1773,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1790,6 +1787,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1890,51 +2046,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -2054,156 +2165,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml index d3266e3c468..9bec573c6e3 100644 --- a/.github/workflows/commit-changes-analyzer.lock.yml +++ b/.github/workflows/commit-changes-analyzer.lock.yml @@ -706,9 +706,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -723,6 +720,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -823,51 +979,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -987,156 +1098,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 1efa5a16702..e1ab252bf0a 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -1111,9 +1111,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1128,6 +1125,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1228,51 +1384,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1392,156 +1503,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index 8685f55a3d3..34057f3b57e 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -1261,9 +1261,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1278,6 +1275,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1378,51 +1534,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1542,156 +1653,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index 40a5a56c517..3370b6ce47d 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -834,9 +834,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -851,6 +848,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -951,51 +1107,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1115,156 +1226,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index 5f10a2b1d24..c355ae72411 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -1944,9 +1944,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1961,6 +1958,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -2061,51 +2217,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -2225,156 +2336,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index 4b2226ffd0e..917590c0870 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -1577,9 +1577,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1594,6 +1591,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1694,51 +1850,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1858,156 +1969,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index 91cd4921681..b66b8ad0f09 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -1289,9 +1289,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1306,6 +1303,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1406,51 +1562,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1570,156 +1681,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index a8e0518384a..5365fa6d859 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -576,9 +576,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -593,6 +590,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -693,51 +849,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -857,156 +968,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml index e5be2a75133..faea0e55fe4 100644 --- a/.github/workflows/daily-file-diet.lock.yml +++ b/.github/workflows/daily-file-diet.lock.yml @@ -619,9 +619,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -636,6 +633,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -736,51 +892,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -900,156 +1011,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index 6a6253a6d52..86b765f9d93 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -962,9 +962,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -979,6 +976,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1079,51 +1235,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1243,156 +1354,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/daily-malicious-code-scan.lock.yml b/.github/workflows/daily-malicious-code-scan.lock.yml index 564680acdf1..7a8b7ad4c5a 100644 --- a/.github/workflows/daily-malicious-code-scan.lock.yml +++ b/.github/workflows/daily-malicious-code-scan.lock.yml @@ -569,9 +569,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -586,6 +583,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -686,51 +842,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -850,156 +961,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index 927e1486837..8f494889b79 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -498,9 +498,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -515,6 +512,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -615,51 +771,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -779,156 +890,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index a65960b3488..f889f21cc57 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -1137,9 +1137,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1154,6 +1151,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1254,51 +1410,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1418,156 +1529,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index 5139e0a30f1..c8f08f53a48 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -1007,9 +1007,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1024,6 +1021,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1124,51 +1280,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1288,156 +1399,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml index 232a82ac011..ce3c29d0d75 100644 --- a/.github/workflows/daily-team-status.lock.yml +++ b/.github/workflows/daily-team-status.lock.yml @@ -398,9 +398,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -415,6 +412,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -515,51 +671,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -679,156 +790,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); @@ -1350,6 +1311,9 @@ jobs: {{#if ${{ github.repository }} }} - **Repository**: `${{ github.repository }}` {{/if}} + {{#if ${{ github.workspace }} }} + - **Workspace**: `${{ github.workspace }}` + {{/if}} {{#if ${{ github.event.issue.number }} }} - **Issue Number**: `#${{ github.event.issue.number }}` {{/if}} diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml index 25ea2c6df44..9e0e4316470 100644 --- a/.github/workflows/dependabot-go-checker.lock.yml +++ b/.github/workflows/dependabot-go-checker.lock.yml @@ -711,9 +711,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -728,6 +725,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -828,51 +984,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -992,156 +1103,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index f76977641a8..d5473c596fd 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -820,9 +820,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -837,6 +834,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -937,51 +1093,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1101,156 +1212,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index e71e3433b12..e2387b45520 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -318,9 +318,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -335,6 +332,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -435,51 +591,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -599,156 +710,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index f159f315013..e6440be4866 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -1092,9 +1092,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1109,6 +1106,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1209,51 +1365,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1373,156 +1484,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml index f1da4c1f15c..192fb78c1d9 100644 --- a/.github/workflows/dictation-prompt.lock.yml +++ b/.github/workflows/dictation-prompt.lock.yml @@ -435,9 +435,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -452,6 +449,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -552,51 +708,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -716,156 +827,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml index 1ad2b04ec8f..3abca15b999 100644 --- a/.github/workflows/docs-noob-tester.lock.yml +++ b/.github/workflows/docs-noob-tester.lock.yml @@ -459,9 +459,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -476,6 +473,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -576,51 +732,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -740,156 +851,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 3a4f4866400..2166c052f31 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -523,9 +523,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -540,6 +537,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -640,51 +796,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -804,156 +915,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index 3dc7f5a1011..40817353d0d 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -510,9 +510,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -527,6 +524,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -627,51 +783,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -791,156 +902,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index cf3b0652ea9..f2f442aad97 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -956,9 +956,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -973,6 +970,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1073,51 +1229,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1237,156 +1348,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index 4227f4d3dee..c5f9b4ee1e1 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -943,9 +943,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -960,6 +957,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1060,51 +1216,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1224,156 +1335,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index 5e8ba8cc815..6b1d6fdaf7e 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -692,9 +692,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -709,6 +706,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -809,51 +965,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -973,156 +1084,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 3928678e485..9ed64226a2f 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -540,9 +540,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -557,6 +554,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -657,51 +813,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -821,156 +932,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index edbbb3996db..d393b9505de 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -1472,9 +1472,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1489,6 +1486,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1589,51 +1745,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1753,156 +1864,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index edea14cefcd..f32e21a7c20 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -575,9 +575,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -592,6 +589,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -692,51 +848,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -856,156 +967,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 91bc9d1dcf9..d38e340237b 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -1379,9 +1379,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1396,6 +1393,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1496,51 +1652,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1660,156 +1771,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index c90b89ff76f..c91e11c81d3 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -828,9 +828,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -845,6 +842,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -945,51 +1101,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1109,156 +1220,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index a72d308dcb9..d84934595d4 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -950,9 +950,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -967,6 +964,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1067,51 +1223,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1231,156 +1342,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index 0fccbb18dce..d1a8be06482 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -922,9 +922,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -939,6 +936,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1039,51 +1195,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1203,156 +1314,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 82febab9fad..188a4625c59 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -319,9 +319,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -336,6 +333,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -436,51 +592,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -600,156 +711,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index a22e5932c03..32d0f16bfa6 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1513,9 +1513,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1530,6 +1527,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1630,51 +1786,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1794,156 +1905,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index ef25ba12b84..0653acbd8ff 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -1034,9 +1034,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1051,6 +1048,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1151,51 +1307,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1315,156 +1426,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 2dc5f841d97..3fffece812b 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1869,9 +1869,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1886,6 +1883,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1986,51 +2142,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -2150,156 +2261,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index c4d5d04b173..553eb368aa5 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -1530,9 +1530,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1547,6 +1544,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1647,51 +1803,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1811,156 +1922,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index a9ae7297e91..3cd23dd7c2b 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -1505,9 +1505,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1522,6 +1519,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1622,51 +1778,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1786,156 +1897,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index 8dfdaeb0f13..0f07795eae3 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -1326,9 +1326,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1343,6 +1340,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1443,51 +1599,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1607,156 +1718,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index d97618283db..385a6451a42 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1773,9 +1773,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1790,6 +1787,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1890,51 +2046,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -2054,156 +2165,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/release-highlights.lock.yml b/.github/workflows/release-highlights.lock.yml index 81d5db0fd92..b560a5f4378 100644 --- a/.github/workflows/release-highlights.lock.yml +++ b/.github/workflows/release-highlights.lock.yml @@ -519,9 +519,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -536,6 +533,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -636,51 +792,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -800,156 +911,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index cb071930254..04897962529 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -479,9 +479,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -496,6 +493,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -596,51 +752,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -760,156 +871,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index f3c1e31a5f7..eba79f082a6 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -931,9 +931,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -948,6 +945,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1048,51 +1204,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1212,156 +1323,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index 01191a287a1..67a87eec351 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -403,9 +403,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -420,6 +417,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -520,51 +676,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -684,156 +795,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index 6b62ee8f03f..f534cebcc28 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -945,9 +945,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -962,6 +959,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1062,51 +1218,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1226,156 +1337,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index 855d231b015..c950e91e6ba 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -832,9 +832,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -849,6 +846,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -949,51 +1105,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1113,156 +1224,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 29d773410ab..103d8cb4458 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1864,9 +1864,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1881,6 +1878,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1981,51 +2137,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -2145,156 +2256,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index 896a58d37cd..700ec17d5a5 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -563,9 +563,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -580,6 +577,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -680,51 +836,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -844,156 +955,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index 4d9eaa224e6..e5a0fb4e6b7 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -908,9 +908,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -925,6 +922,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1025,51 +1181,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1189,156 +1300,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index b1bd45a01c8..9ae27e08d5b 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -966,9 +966,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -983,6 +980,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1083,51 +1239,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1247,156 +1358,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index d91acb04157..50ab507b3dc 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -742,9 +742,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -759,6 +756,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -859,51 +1015,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1023,156 +1134,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 648c5a3cdd5..ad61c34b254 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -750,9 +750,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -767,6 +764,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -867,51 +1023,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1031,156 +1142,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/smoke-detector.lock.yml b/.github/workflows/smoke-detector.lock.yml index 03193627d64..1d3500d4547 100644 --- a/.github/workflows/smoke-detector.lock.yml +++ b/.github/workflows/smoke-detector.lock.yml @@ -1538,9 +1538,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1555,6 +1552,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1655,51 +1811,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1819,156 +1930,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index 098e410a91a..92660f0ff33 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -856,9 +856,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -873,6 +870,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -973,51 +1129,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1137,156 +1248,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index 6e4aaef2e22..e796789f738 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -517,9 +517,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -534,6 +531,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -634,51 +790,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -798,156 +909,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index c5378e72807..b079cf864d2 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -1161,9 +1161,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1178,6 +1175,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1278,51 +1434,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1442,156 +1553,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/test-assign-milestone-allowed.lock.yml b/.github/workflows/test-assign-milestone-allowed.lock.yml index 03cfb3b20ab..d8311dab0c1 100644 --- a/.github/workflows/test-assign-milestone-allowed.lock.yml +++ b/.github/workflows/test-assign-milestone-allowed.lock.yml @@ -398,9 +398,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -415,6 +412,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -515,51 +671,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -679,156 +790,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/test-claude-assign-milestone.lock.yml b/.github/workflows/test-claude-assign-milestone.lock.yml index 5a4c9d42abe..a74f72caf89 100644 --- a/.github/workflows/test-claude-assign-milestone.lock.yml +++ b/.github/workflows/test-claude-assign-milestone.lock.yml @@ -392,9 +392,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -409,6 +406,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -509,51 +665,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -673,156 +784,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/test-close-discussion.lock.yml b/.github/workflows/test-close-discussion.lock.yml index 4a34058acbe..584e772c6ea 100644 --- a/.github/workflows/test-close-discussion.lock.yml +++ b/.github/workflows/test-close-discussion.lock.yml @@ -304,9 +304,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -321,6 +318,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -421,51 +577,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -585,156 +696,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/test-codex-assign-milestone.lock.yml b/.github/workflows/test-codex-assign-milestone.lock.yml index 732ddfb4b9e..9b0677c63b1 100644 --- a/.github/workflows/test-codex-assign-milestone.lock.yml +++ b/.github/workflows/test-codex-assign-milestone.lock.yml @@ -283,9 +283,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -300,6 +297,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -400,51 +556,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -564,156 +675,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/test-copilot-assign-milestone.lock.yml b/.github/workflows/test-copilot-assign-milestone.lock.yml index 4288cb93d14..7612d55b671 100644 --- a/.github/workflows/test-copilot-assign-milestone.lock.yml +++ b/.github/workflows/test-copilot-assign-milestone.lock.yml @@ -283,9 +283,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -300,6 +297,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -400,51 +556,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -564,156 +675,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/test-ollama-threat-detection.lock.yml b/.github/workflows/test-ollama-threat-detection.lock.yml index e47993ea80e..cc3beb3585a 100644 --- a/.github/workflows/test-ollama-threat-detection.lock.yml +++ b/.github/workflows/test-ollama-threat-detection.lock.yml @@ -289,9 +289,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -306,6 +303,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -406,51 +562,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -570,156 +681,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index d0595bcce2f..2783a678980 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -733,9 +733,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -750,6 +747,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -850,51 +1006,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1014,156 +1125,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index 7707803243d..2fa57ac89ce 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -956,9 +956,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -973,6 +970,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1073,51 +1229,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1237,156 +1348,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index f7cf76f9e77..514613ee10e 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1516,9 +1516,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -1533,6 +1530,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1633,51 +1789,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1797,156 +1908,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index 1b80c7b0c6f..42b159c0a99 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -563,9 +563,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -580,6 +577,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -680,51 +836,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -844,156 +955,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 1f1800601ea..0b3bf7e46dc 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -914,9 +914,6 @@ jobs: const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); - const encoder = new TextEncoder(); - const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; - const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); function normalizeBranchName(branchName) { if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { return branchName; @@ -931,6 +928,165 @@ jobs: normalized = normalized.toLowerCase(); return normalized; } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + return `${typeof parsed}`; + } catch { + return "text content"; + } + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + const description = generateCompactSchema(content); + return { + filename: filename, + description: description, + }; + } + function getCurrentBranch() { + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + } + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + if (ghHeadRef) { + return ghHeadRef; + } + if (ghRefName) { + return ghRefName; + } + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); + } + function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; + } + function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + let patchGenerated = false; + let errorMessage = null; + try { + if (branchName) { + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + let baseRef; + try { + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + } + } + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + } else { + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + if (commitCount > 0) { + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + if (!patchContent.trim()) { + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; + } + const encoder = new TextEncoder(); + const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; + const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; debug(`Reading config from file: ${configPath}`); @@ -1031,51 +1187,6 @@ jobs: }; writeMessage(res); } - function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); - } - function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - return `${typeof parsed}`; - } catch { - return "text content"; - } - } - function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const hash = crypto.createHash("sha256").update(content).digest("hex"); - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - fs.writeFileSync(filepath, content, "utf8"); - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - const description = generateCompactSchema(content); - return { - filename: filename, - description: description, - }; - } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1195,156 +1306,6 @@ jobs: ], }; }; - function getCurrentBranch() { - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); - } - function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; - } - function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - let patchGenerated = false; - let errorMessage = null; - try { - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - let baseRef; - try { - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - if (commitCount > 0) { - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - if (!patchContent.trim()) { - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; - } const createPullRequestHandler = args => { const entry = { ...args, type: "create_pull_request" }; const baseBranch = getBaseBranch(); diff --git a/Makefile b/Makefile index 798f8717ff4..b235af85ba6 100644 --- a/Makefile +++ b/Makefile @@ -102,7 +102,7 @@ fuzz: # Test JavaScript files .PHONY: test-js test-js: build-js - cd pkg/workflow/js && npm run test:js + cd pkg/workflow/js && npm run test:js -- --no-file-parallelism .PHONY: build-js build-js: diff --git a/pkg/workflow/bundler.go b/pkg/workflow/bundler.go index c193a0c3ede..28a18a3a45f 100644 --- a/pkg/workflow/bundler.go +++ b/pkg/workflow/bundler.go @@ -16,7 +16,7 @@ var bundlerLog = logger.New("workflow:bundler") // mainContent is the main JavaScript content that may contain require() calls // basePath is the base directory path for resolving relative imports (e.g., "js") func BundleJavaScriptFromSources(mainContent string, sources map[string]string, basePath string) (string, error) { - bundlerLog.Printf("Bundling JavaScript: source_count=%d, base_path=%s", len(sources), basePath) + bundlerLog.Printf("Bundling JavaScript: source_count=%d, base_path=%s, main_content_size=%d bytes", len(sources), basePath, len(mainContent)) // Track already processed files to avoid circular dependencies processed := make(map[string]bool) @@ -37,7 +37,17 @@ func BundleJavaScriptFromSources(mainContent string, sources map[string]string, return "", err } - bundlerLog.Printf("Bundling completed: processed_files=%d, output_size=%d bytes", len(processed), len(bundled)) + // Log size information about the bundled output + lines := strings.Split(bundled, "\n") + var maxLineLength int + for _, line := range lines { + if len(line) > maxLineLength { + maxLineLength = len(line) + } + } + + bundlerLog.Printf("Bundling completed: processed_files=%d, output_size=%d bytes, output_lines=%d, max_line_length=%d chars", + len(processed), len(bundled), len(lines), maxLineLength) return bundled, nil } @@ -111,6 +121,8 @@ func bundleFromSources(content string, currentPath string, sources map[string]st return "", fmt.Errorf("required file not found in sources: %s", fullPath) } + bundlerLog.Printf("Inlining file: %s (size: %d bytes)", fullPath, len(requiredContent)) + // Recursively bundle the required file requiredDir := filepath.Dir(fullPath) bundledRequired, err := bundleFromSources(requiredContent, requiredDir, sources, processed) @@ -120,6 +132,7 @@ func bundleFromSources(content string, currentPath string, sources map[string]st // Remove exports from the bundled content cleanedRequired := removeExports(bundledRequired) + bundlerLog.Printf("Processed %s: original_size=%d, after_export_removal=%d", fullPath, len(bundledRequired), len(cleanedRequired)) // Add a comment indicating the inlined file result.WriteString(fmt.Sprintf("// === Inlined from %s ===\n", requirePath)) diff --git a/pkg/workflow/js.go b/pkg/workflow/js.go index 9120909846b..c939c4266c3 100644 --- a/pkg/workflow/js.go +++ b/pkg/workflow/js.go @@ -69,7 +69,31 @@ var validateErrorsScript string var missingToolScript string //go:embed js/safe_outputs_mcp_server.cjs -var safeOutputsMCPServerScript string +var safeOutputsMCPServerScriptSource string + +var ( + safeOutputsMCPServerScript string + safeOutputsMCPServerScriptOnce sync.Once +) + +// getSafeOutputsMCPServerScript returns the bundled safe_outputs_mcp_server script +// Bundling is performed on first access and cached for subsequent calls +func getSafeOutputsMCPServerScript() string { + safeOutputsMCPServerScriptOnce.Do(func() { + jsLog.Print("Bundling safe_outputs_mcp_server script") + sources := GetJavaScriptSources() + bundled, err := BundleJavaScriptFromSources(safeOutputsMCPServerScriptSource, sources, "") + if err != nil { + jsLog.Printf("Failed to bundle safe_outputs_mcp_server script, using source as-is: %v", err) + // If bundling fails, use the source as-is + safeOutputsMCPServerScript = safeOutputsMCPServerScriptSource + } else { + jsLog.Printf("Successfully bundled safe_outputs_mcp_server script: %d bytes", len(bundled)) + safeOutputsMCPServerScript = bundled + } + }) + return safeOutputsMCPServerScript +} //go:embed js/safe_outputs_tools.json var safeOutputsToolsJSON string @@ -152,25 +176,53 @@ var getRepositoryUrlScript string //go:embed js/check_permissions_utils.cjs var checkPermissionsUtilsScript string +//go:embed js/normalize_branch_name.cjs +var normalizeBranchNameScript string + +//go:embed js/estimate_tokens.cjs +var estimateTokensScript string + +//go:embed js/generate_compact_schema.cjs +var generateCompactSchemaScript string + +//go:embed js/write_large_content_to_file.cjs +var writeLargeContentToFileScript string + +//go:embed js/get_current_branch.cjs +var getCurrentBranchScript string + +//go:embed js/get_base_branch.cjs +var getBaseBranchScript string + +//go:embed js/generate_git_patch.cjs +var generateGitPatchJSScript string + // GetJavaScriptSources returns a map of all embedded JavaScript sources // The keys are the relative paths from the js directory func GetJavaScriptSources() map[string]string { return map[string]string{ - "sanitize_content.cjs": sanitizeContentScript, - "sanitize_label_content.cjs": sanitizeLabelContentScript, - "sanitize_workflow_name.cjs": sanitizeWorkflowNameScript, - "load_agent_output.cjs": loadAgentOutputScript, - "staged_preview.cjs": stagedPreviewScript, - "safe_output_helpers.cjs": safeOutputHelpersScript, - "safe_output_validator.cjs": safeOutputValidatorScript, - "is_truthy.cjs": isTruthyScript, - "log_parser_bootstrap.cjs": logParserBootstrapScript, - "log_parser_shared.cjs": logParserSharedScript, - "update_activation_comment.cjs": updateActivationCommentScript, - "generate_footer.cjs": generateFooterScript, - "get_tracker_id.cjs": getTrackerIDScript, - "get_repository_url.cjs": getRepositoryUrlScript, - "check_permissions_utils.cjs": checkPermissionsUtilsScript, + "sanitize_content.cjs": sanitizeContentScript, + "sanitize_label_content.cjs": sanitizeLabelContentScript, + "sanitize_workflow_name.cjs": sanitizeWorkflowNameScript, + "load_agent_output.cjs": loadAgentOutputScript, + "staged_preview.cjs": stagedPreviewScript, + "safe_output_helpers.cjs": safeOutputHelpersScript, + "safe_output_validator.cjs": safeOutputValidatorScript, + "is_truthy.cjs": isTruthyScript, + "log_parser_bootstrap.cjs": logParserBootstrapScript, + "log_parser_shared.cjs": logParserSharedScript, + "update_activation_comment.cjs": updateActivationCommentScript, + "generate_footer.cjs": generateFooterScript, + "get_tracker_id.cjs": getTrackerIDScript, + "get_repository_url.cjs": getRepositoryUrlScript, + "check_permissions_utils.cjs": checkPermissionsUtilsScript, + "normalize_branch_name.cjs": normalizeBranchNameScript, + "estimate_tokens.cjs": estimateTokensScript, + "generate_compact_schema.cjs": generateCompactSchemaScript, + "write_large_content_to_file.cjs": writeLargeContentToFileScript, + "get_current_branch.cjs": getCurrentBranchScript, + "get_base_branch.cjs": getBaseBranchScript, + "generate_git_patch.cjs": generateGitPatchJSScript, } } @@ -617,7 +669,7 @@ func GetLogParserBootstrap() string { // GetSafeOutputsMCPServerScript returns the JavaScript content for the GitHub Agentic Workflows Safe Outputs MCP server func GetSafeOutputsMCPServerScript() string { - return safeOutputsMCPServerScript + return getSafeOutputsMCPServerScript() } // GetSafeOutputsToolsJSON returns the JSON content for the safe outputs tools definitions diff --git a/pkg/workflow/js/estimate_tokens.cjs b/pkg/workflow/js/estimate_tokens.cjs new file mode 100644 index 00000000000..a5fc23d08be --- /dev/null +++ b/pkg/workflow/js/estimate_tokens.cjs @@ -0,0 +1,16 @@ +// @ts-check +/// + +/** + * Estimates token count from text using 4 chars per token estimate + * @param {string} text - The text to estimate tokens for + * @returns {number} Approximate token count + */ +function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); +} + +module.exports = { + estimateTokens, +}; diff --git a/pkg/workflow/js/estimate_tokens.test.cjs b/pkg/workflow/js/estimate_tokens.test.cjs new file mode 100644 index 00000000000..8d2d31d7471 --- /dev/null +++ b/pkg/workflow/js/estimate_tokens.test.cjs @@ -0,0 +1,52 @@ +import { describe, it, expect } from "vitest"; + +describe("estimateTokens", () => { + it("should estimate tokens for text", async () => { + const { estimateTokens } = await import("./estimate_tokens.cjs"); + + expect(estimateTokens("hello")).toBe(2); // 5 chars / 4 = 1.25, ceil = 2 + expect(estimateTokens("test")).toBe(1); // 4 chars / 4 = 1 + expect(estimateTokens("testing")).toBe(2); // 7 chars / 4 = 1.75, ceil = 2 + }); + + it("should handle empty strings and null", async () => { + const { estimateTokens } = await import("./estimate_tokens.cjs"); + + expect(estimateTokens("")).toBe(0); + expect(estimateTokens(null)).toBe(0); + expect(estimateTokens(undefined)).toBe(0); + }); + + it("should handle long text", async () => { + const { estimateTokens } = await import("./estimate_tokens.cjs"); + + const longText = "a".repeat(1000); + expect(estimateTokens(longText)).toBe(250); // 1000 / 4 = 250 + }); + + it("should round up using Math.ceil", async () => { + const { estimateTokens } = await import("./estimate_tokens.cjs"); + + expect(estimateTokens("a")).toBe(1); // 1 / 4 = 0.25, ceil = 1 + expect(estimateTokens("ab")).toBe(1); // 2 / 4 = 0.5, ceil = 1 + expect(estimateTokens("abc")).toBe(1); // 3 / 4 = 0.75, ceil = 1 + expect(estimateTokens("abcd")).toBe(1); // 4 / 4 = 1 + expect(estimateTokens("abcde")).toBe(2); // 5 / 4 = 1.25, ceil = 2 + }); + + it("should handle multi-byte characters", async () => { + const { estimateTokens } = await import("./estimate_tokens.cjs"); + + // Note: This uses string length, not byte length + expect(estimateTokens("😀")).toBe(1); // 1 char (even though it's 4 bytes) + expect(estimateTokens("你好世界")).toBe(1); // 4 chars / 4 = 1 + }); + + it("should handle whitespace and special characters", async () => { + const { estimateTokens } = await import("./estimate_tokens.cjs"); + + expect(estimateTokens(" ")).toBe(1); // 3 spaces / 4 = 0.75, ceil = 1 + expect(estimateTokens("\n\n\n\n")).toBe(1); // 4 newlines / 4 = 1 + expect(estimateTokens("!@#$%")).toBe(2); // 5 special chars / 4 = 1.25, ceil = 2 + }); +}); diff --git a/pkg/workflow/js/generate_compact_schema.cjs b/pkg/workflow/js/generate_compact_schema.cjs new file mode 100644 index 00000000000..f990c75e2c3 --- /dev/null +++ b/pkg/workflow/js/generate_compact_schema.cjs @@ -0,0 +1,43 @@ +// @ts-check +/// + +/** + * Generates a compact schema description from JSON content + * @param {string} content - The JSON content to analyze + * @returns {string} Compact schema description for jq/agent + */ +function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + + // Generate a compact schema based on the structure + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + // For arrays, describe the first element's structure + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + // For objects, list top-level keys + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; + } + + return `${typeof parsed}`; + } catch { + // If not valid JSON, return generic description + return "text content"; + } +} + +module.exports = { + generateCompactSchema, +}; diff --git a/pkg/workflow/js/generate_compact_schema.test.cjs b/pkg/workflow/js/generate_compact_schema.test.cjs new file mode 100644 index 00000000000..0fec93ef508 --- /dev/null +++ b/pkg/workflow/js/generate_compact_schema.test.cjs @@ -0,0 +1,86 @@ +import { describe, it, expect } from "vitest"; + +describe("generateCompactSchema", () => { + it("should handle empty arrays", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + expect(generateCompactSchema("[]")).toBe("[]"); + }); + + it("should describe array of objects", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + const json = JSON.stringify([ + { id: 1, name: "test", value: 10 }, + { id: 2, name: "test2", value: 20 }, + ]); + expect(generateCompactSchema(json)).toBe("[{id, name, value}] (2 items)"); + }); + + it("should describe array of primitives", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + expect(generateCompactSchema("[1, 2, 3]")).toBe("[number] (3 items)"); + expect(generateCompactSchema('["a", "b", "c"]')).toBe("[string] (3 items)"); + expect(generateCompactSchema("[true, false]")).toBe("[boolean] (2 items)"); + }); + + it("should describe objects with few keys", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + const json = JSON.stringify({ id: 1, name: "test", value: 10 }); + expect(generateCompactSchema(json)).toBe("{id, name, value}"); + }); + + it("should describe objects with many keys", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + const obj = {}; + for (let i = 0; i < 15; i++) { + obj[`key${i}`] = i; + } + const json = JSON.stringify(obj); + const schema = generateCompactSchema(json); + expect(schema).toMatch(/^\{key0, key1, .+\.\.\.\} \(15 keys\)$/); + }); + + it("should handle primitives", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + expect(generateCompactSchema("123")).toBe("number"); + expect(generateCompactSchema('"hello"')).toBe("string"); + expect(generateCompactSchema("true")).toBe("boolean"); + expect(generateCompactSchema("null")).toBe("object"); // JSON.parse(null) is object + }); + + it("should handle invalid JSON", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + expect(generateCompactSchema("not valid json")).toBe("text content"); + expect(generateCompactSchema("")).toBe("text content"); + expect(generateCompactSchema("{invalid}")).toBe("text content"); + }); + + it("should handle nested structures", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + const json = JSON.stringify({ + users: [{ id: 1, name: "test" }], + meta: { total: 1 }, + }); + expect(generateCompactSchema(json)).toBe("{users, meta}"); + }); + + it("should handle single item arrays", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + expect(generateCompactSchema("[1]")).toBe("[number] (1 items)"); + expect(generateCompactSchema('[{"id": 1}]')).toBe("[{id}] (1 items)"); + }); + + it("should handle empty objects", async () => { + const { generateCompactSchema } = await import("./generate_compact_schema.cjs"); + + expect(generateCompactSchema("{}")).toBe("{}"); + }); +}); diff --git a/pkg/workflow/js/generate_git_patch.cjs b/pkg/workflow/js/generate_git_patch.cjs new file mode 100644 index 00000000000..af9654f28f0 --- /dev/null +++ b/pkg/workflow/js/generate_git_patch.cjs @@ -0,0 +1,141 @@ +// @ts-check +/// + +const fs = require("fs"); +const path = require("path"); +const { execSync } = require("child_process"); + +const { getBaseBranch } = require("./get_base_branch.cjs"); + +/** + * Generates a git patch file for the current changes + * @param {string} branchName - The branch name to generate patch for + * @returns {Object} Object with patch info or error + */ +function generateGitPatch(branchName) { + const patchPath = "/tmp/gh-aw/aw.patch"; + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); + const githubSha = process.env.GITHUB_SHA; + + // Ensure /tmp/gh-aw directory exists + const patchDir = path.dirname(patchPath); + if (!fs.existsSync(patchDir)) { + fs.mkdirSync(patchDir, { recursive: true }); + } + + let patchGenerated = false; + let errorMessage = null; + + try { + // Strategy 1: If we have a branch name, check if that branch exists and get its diff + if (branchName) { + // Check if the branch exists locally + try { + execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); + + // Determine base ref for patch generation + let baseRef; + try { + // Check if origin/branchName exists + execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); + baseRef = `origin/${branchName}`; + } catch { + // Use merge-base with default branch + execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); + baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); + } + + // Count commits to be included + const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); + + if (commitCount > 0) { + // Generate patch from the determined base to the branch + const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { + cwd, + encoding: "utf8", + }); + + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch (branchError) { + // Branch does not exist locally + } + } + + // Strategy 2: Check if commits were made to current HEAD since checkout + if (!patchGenerated) { + const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); + + if (!githubSha) { + errorMessage = "GITHUB_SHA environment variable is not set"; + } else if (currentHead === githubSha) { + // No commits have been made since checkout + } else { + // Check if GITHUB_SHA is an ancestor of current HEAD + try { + execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); + + // Count commits between GITHUB_SHA and HEAD + const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); + + if (commitCount > 0) { + // Generate patch from GITHUB_SHA to HEAD + const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { + cwd, + encoding: "utf8", + }); + + if (patchContent && patchContent.trim()) { + fs.writeFileSync(patchPath, patchContent, "utf8"); + patchGenerated = true; + } + } + } catch { + // GITHUB_SHA is not an ancestor of HEAD - repository state has diverged + } + } + } + } catch (error) { + errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + } + + // Check if patch was generated and has content + if (patchGenerated && fs.existsSync(patchPath)) { + const patchContent = fs.readFileSync(patchPath, "utf8"); + const patchSize = Buffer.byteLength(patchContent, "utf8"); + const patchLines = patchContent.split("\n").length; + + if (!patchContent.trim()) { + // Empty patch + return { + success: false, + error: "No changes to commit - patch is empty", + patchPath: patchPath, + patchSize: 0, + patchLines: 0, + }; + } + + return { + success: true, + patchPath: patchPath, + patchSize: patchSize, + patchLines: patchLines, + }; + } + + // No patch generated + return { + success: false, + error: errorMessage || "No changes to commit - no commits found", + patchPath: patchPath, + }; +} + +module.exports = { + generateGitPatch, +}; diff --git a/pkg/workflow/js/generate_git_patch.test.cjs b/pkg/workflow/js/generate_git_patch.test.cjs new file mode 100644 index 00000000000..403838e4a80 --- /dev/null +++ b/pkg/workflow/js/generate_git_patch.test.cjs @@ -0,0 +1,129 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; + +describe("generateGitPatch", () => { + let originalEnv; + + beforeEach(() => { + // Save original environment + originalEnv = { + GITHUB_SHA: process.env.GITHUB_SHA, + GITHUB_WORKSPACE: process.env.GITHUB_WORKSPACE, + DEFAULT_BRANCH: process.env.DEFAULT_BRANCH, + GH_AW_BASE_BRANCH: process.env.GH_AW_BASE_BRANCH, + }; + }); + + afterEach(() => { + // Restore original environment + Object.keys(originalEnv).forEach(key => { + if (originalEnv[key] !== undefined) { + process.env[key] = originalEnv[key]; + } else { + delete process.env[key]; + } + }); + }); + + it("should return error when no commits can be found", async () => { + delete process.env.GITHUB_SHA; + process.env.GITHUB_WORKSPACE = "/tmp/test-repo"; + + const { generateGitPatch } = await import("./generate_git_patch.cjs"); + + const result = generateGitPatch(null); + + expect(result.success).toBe(false); + expect(result).toHaveProperty("error"); + }); + + it("should return success false when no commits found", async () => { + const { generateGitPatch } = await import("./generate_git_patch.cjs"); + + // Set up environment but in a way that won't find commits + process.env.GITHUB_WORKSPACE = "/tmp/nonexistent-repo"; + process.env.GITHUB_SHA = "abc123"; + + const result = generateGitPatch("nonexistent-branch"); + + expect(result.success).toBe(false); + expect(result).toHaveProperty("error"); + expect(result).toHaveProperty("patchPath"); + }); + + it("should create patch directory if it doesn't exist", async () => { + const { generateGitPatch } = await import("./generate_git_patch.cjs"); + + process.env.GITHUB_WORKSPACE = "/tmp/nonexistent-repo"; + process.env.GITHUB_SHA = "abc123"; + + // Even if it fails, it should try to create the directory + const result = generateGitPatch("test-branch"); + + expect(result).toHaveProperty("patchPath"); + expect(result.patchPath).toBe("/tmp/gh-aw/aw.patch"); + }); + + it("should return patch info structure", async () => { + const { generateGitPatch } = await import("./generate_git_patch.cjs"); + + process.env.GITHUB_WORKSPACE = "/tmp/nonexistent-repo"; + process.env.GITHUB_SHA = "abc123"; + + const result = generateGitPatch("test-branch"); + + expect(result).toHaveProperty("success"); + expect(result).toHaveProperty("patchPath"); + expect(typeof result.success).toBe("boolean"); + }); + + it("should handle null branch name", async () => { + const { generateGitPatch } = await import("./generate_git_patch.cjs"); + + process.env.GITHUB_WORKSPACE = "/tmp/nonexistent-repo"; + process.env.GITHUB_SHA = "abc123"; + + const result = generateGitPatch(null); + + expect(result).toHaveProperty("success"); + expect(result).toHaveProperty("patchPath"); + }); + + it("should handle empty branch name", async () => { + const { generateGitPatch } = await import("./generate_git_patch.cjs"); + + process.env.GITHUB_WORKSPACE = "/tmp/nonexistent-repo"; + process.env.GITHUB_SHA = "abc123"; + + const result = generateGitPatch(""); + + expect(result).toHaveProperty("success"); + expect(result).toHaveProperty("patchPath"); + }); + + it("should use default branch from environment", async () => { + const { generateGitPatch } = await import("./generate_git_patch.cjs"); + + process.env.GITHUB_WORKSPACE = "/tmp/nonexistent-repo"; + process.env.GITHUB_SHA = "abc123"; + process.env.DEFAULT_BRANCH = "develop"; + + const result = generateGitPatch("feature-branch"); + + expect(result).toHaveProperty("success"); + // Should attempt to use develop as default branch + }); + + it("should fall back to GH_AW_BASE_BRANCH if DEFAULT_BRANCH not set", async () => { + const { generateGitPatch } = await import("./generate_git_patch.cjs"); + + process.env.GITHUB_WORKSPACE = "/tmp/nonexistent-repo"; + process.env.GITHUB_SHA = "abc123"; + delete process.env.DEFAULT_BRANCH; + process.env.GH_AW_BASE_BRANCH = "master"; + + const result = generateGitPatch("feature-branch"); + + expect(result).toHaveProperty("success"); + // Should attempt to use master as base branch + }); +}); diff --git a/pkg/workflow/js/get_base_branch.cjs b/pkg/workflow/js/get_base_branch.cjs new file mode 100644 index 00000000000..ded46f56b50 --- /dev/null +++ b/pkg/workflow/js/get_base_branch.cjs @@ -0,0 +1,14 @@ +// @ts-check +/// + +/** + * Get the base branch name from environment variable + * @returns {string} The base branch name (defaults to "main") + */ +function getBaseBranch() { + return process.env.GH_AW_BASE_BRANCH || "main"; +} + +module.exports = { + getBaseBranch, +}; diff --git a/pkg/workflow/js/get_base_branch.test.cjs b/pkg/workflow/js/get_base_branch.test.cjs new file mode 100644 index 00000000000..329f5966eef --- /dev/null +++ b/pkg/workflow/js/get_base_branch.test.cjs @@ -0,0 +1,54 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; + +describe("getBaseBranch", () => { + let originalEnv; + + beforeEach(() => { + // Save original environment + originalEnv = process.env.GH_AW_BASE_BRANCH; + }); + + afterEach(() => { + // Restore original environment + if (originalEnv !== undefined) { + process.env.GH_AW_BASE_BRANCH = originalEnv; + } else { + delete process.env.GH_AW_BASE_BRANCH; + } + }); + + it("should return main by default", async () => { + delete process.env.GH_AW_BASE_BRANCH; + const { getBaseBranch } = await import("./get_base_branch.cjs"); + + expect(getBaseBranch()).toBe("main"); + }); + + it("should return environment variable value", async () => { + process.env.GH_AW_BASE_BRANCH = "develop"; + const { getBaseBranch } = await import("./get_base_branch.cjs"); + + expect(getBaseBranch()).toBe("develop"); + }); + + it("should handle various branch names", async () => { + const { getBaseBranch } = await import("./get_base_branch.cjs"); + + process.env.GH_AW_BASE_BRANCH = "master"; + expect(getBaseBranch()).toBe("master"); + + process.env.GH_AW_BASE_BRANCH = "release/v1.0"; + expect(getBaseBranch()).toBe("release/v1.0"); + + process.env.GH_AW_BASE_BRANCH = "feature/new-feature"; + expect(getBaseBranch()).toBe("feature/new-feature"); + }); + + it("should return main if environment variable is empty string", async () => { + process.env.GH_AW_BASE_BRANCH = ""; + const { getBaseBranch } = await import("./get_base_branch.cjs"); + + // Empty string is falsy, so || operator returns "main" + expect(getBaseBranch()).toBe("main"); + }); +}); diff --git a/pkg/workflow/js/get_current_branch.cjs b/pkg/workflow/js/get_current_branch.cjs new file mode 100644 index 00000000000..b0a5e0f2a2e --- /dev/null +++ b/pkg/workflow/js/get_current_branch.cjs @@ -0,0 +1,44 @@ +// @ts-check +/// + +const { execSync } = require("child_process"); + +/** + * Get the current git branch name + * @returns {string} The current branch name + */ +function getCurrentBranch() { + // Priority 1: Try git command first to get the actual checked-out branch + // This is more reliable than environment variables which may not reflect + // branch changes made during the workflow execution + const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); + try { + const branch = execSync("git rev-parse --abbrev-ref HEAD", { + encoding: "utf8", + cwd: cwd, + }).trim(); + return branch; + } catch (error) { + // Ignore error and try fallback + } + + // Priority 2: Fallback to GitHub Actions environment variables + // GITHUB_HEAD_REF is set for pull_request events and contains the source branch name + // GITHUB_REF_NAME is set for all events and contains the branch/tag name + const ghHeadRef = process.env.GITHUB_HEAD_REF; + const ghRefName = process.env.GITHUB_REF_NAME; + + if (ghHeadRef) { + return ghHeadRef; + } + + if (ghRefName) { + return ghRefName; + } + + throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); +} + +module.exports = { + getCurrentBranch, +}; diff --git a/pkg/workflow/js/get_current_branch.test.cjs b/pkg/workflow/js/get_current_branch.test.cjs new file mode 100644 index 00000000000..19126360e39 --- /dev/null +++ b/pkg/workflow/js/get_current_branch.test.cjs @@ -0,0 +1,96 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; + +describe("getCurrentBranch", () => { + let originalEnv; + + beforeEach(() => { + // Save original environment + originalEnv = { + GITHUB_HEAD_REF: process.env.GITHUB_HEAD_REF, + GITHUB_REF_NAME: process.env.GITHUB_REF_NAME, + GITHUB_WORKSPACE: process.env.GITHUB_WORKSPACE, + }; + + // Clean environment for tests + delete process.env.GITHUB_HEAD_REF; + delete process.env.GITHUB_REF_NAME; + delete process.env.GITHUB_WORKSPACE; + }); + + afterEach(() => { + // Restore original environment + if (originalEnv.GITHUB_HEAD_REF !== undefined) { + process.env.GITHUB_HEAD_REF = originalEnv.GITHUB_HEAD_REF; + } + if (originalEnv.GITHUB_REF_NAME !== undefined) { + process.env.GITHUB_REF_NAME = originalEnv.GITHUB_REF_NAME; + } + if (originalEnv.GITHUB_WORKSPACE !== undefined) { + process.env.GITHUB_WORKSPACE = originalEnv.GITHUB_WORKSPACE; + } + }); + + it("should return GITHUB_HEAD_REF if set", async () => { + process.env.GITHUB_HEAD_REF = "feature/test-branch"; + process.env.GITHUB_REF_NAME = "other-branch"; + + const { getCurrentBranch } = await import("./get_current_branch.cjs"); + + // If git command fails, should use GITHUB_HEAD_REF + try { + const result = getCurrentBranch(); + // Either from git or from GITHUB_HEAD_REF + expect(typeof result).toBe("string"); + expect(result.length).toBeGreaterThan(0); + } catch (error) { + // This is acceptable if we're not in a git repo + expect(error.message).toContain("Failed to determine current branch"); + } + }); + + it("should return GITHUB_REF_NAME if GITHUB_HEAD_REF not set", async () => { + delete process.env.GITHUB_HEAD_REF; + process.env.GITHUB_REF_NAME = "main"; + + const { getCurrentBranch } = await import("./get_current_branch.cjs"); + + try { + const result = getCurrentBranch(); + // Either from git or from GITHUB_REF_NAME + expect(typeof result).toBe("string"); + expect(result.length).toBeGreaterThan(0); + } catch (error) { + // This is acceptable if we're not in a git repo + expect(error.message).toContain("Failed to determine current branch"); + } + }); + + it("should throw error when no branch can be determined", async () => { + delete process.env.GITHUB_HEAD_REF; + delete process.env.GITHUB_REF_NAME; + process.env.GITHUB_WORKSPACE = "/tmp/nonexistent-git-repo"; + + const { getCurrentBranch } = await import("./get_current_branch.cjs"); + + expect(() => getCurrentBranch()).toThrow("Failed to determine current branch"); + }); + + it("should prioritize GITHUB_HEAD_REF over GITHUB_REF_NAME", async () => { + process.env.GITHUB_HEAD_REF = "pr-branch"; + process.env.GITHUB_REF_NAME = "main"; + process.env.GITHUB_WORKSPACE = "/tmp/nonexistent-git-repo"; + + const { getCurrentBranch } = await import("./get_current_branch.cjs"); + + try { + const result = getCurrentBranch(); + // If git fails, should fall back to GITHUB_HEAD_REF + if (result === "pr-branch" || result === "main") { + expect(result).toBeTruthy(); + } + } catch (error) { + // This is acceptable if we're not in a git repo + expect(error.message).toContain("Failed to determine current branch"); + } + }); +}); diff --git a/pkg/workflow/js/normalize_branch_name.cjs b/pkg/workflow/js/normalize_branch_name.cjs new file mode 100644 index 00000000000..ce8d4473a3e --- /dev/null +++ b/pkg/workflow/js/normalize_branch_name.cjs @@ -0,0 +1,54 @@ +// @ts-check +/// + +/** + * Normalizes a branch name to be a valid git branch name. + * + * IMPORTANT: Keep this function in sync with the normalizeBranchName function in upload_assets.cjs + * + * Valid characters: alphanumeric (a-z, A-Z, 0-9), dash (-), underscore (_), forward slash (/), dot (.) + * Max length: 128 characters + * + * The normalization process: + * 1. Replaces invalid characters with a single dash + * 2. Collapses multiple consecutive dashes to a single dash + * 3. Removes leading and trailing dashes + * 4. Truncates to 128 characters + * 5. Removes trailing dashes after truncation + * 6. Converts to lowercase + * + * @param {string} branchName - The branch name to normalize + * @returns {string} The normalized branch name + */ +function normalizeBranchName(branchName) { + if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { + return branchName; + } + + // Replace any sequence of invalid characters with a single dash + // Valid characters are: a-z, A-Z, 0-9, -, _, /, . + let normalized = branchName.replace(/[^a-zA-Z0-9\-_/.]+/g, "-"); + + // Collapse multiple consecutive dashes to a single dash + normalized = normalized.replace(/-+/g, "-"); + + // Remove leading and trailing dashes + normalized = normalized.replace(/^-+|-+$/g, ""); + + // Truncate to max 128 characters + if (normalized.length > 128) { + normalized = normalized.substring(0, 128); + } + + // Ensure it doesn't end with a dash after truncation + normalized = normalized.replace(/-+$/, ""); + + // Convert to lowercase + normalized = normalized.toLowerCase(); + + return normalized; +} + +module.exports = { + normalizeBranchName, +}; diff --git a/pkg/workflow/js/normalize_branch_name.test.cjs b/pkg/workflow/js/normalize_branch_name.test.cjs new file mode 100644 index 00000000000..b45c693eed4 --- /dev/null +++ b/pkg/workflow/js/normalize_branch_name.test.cjs @@ -0,0 +1,82 @@ +import { describe, it, expect } from "vitest"; + +describe("normalizeBranchName", () => { + it("should handle valid branch names", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + expect(normalizeBranchName("feature/add-login")).toBe("feature/add-login"); + expect(normalizeBranchName("my-branch")).toBe("my-branch"); + expect(normalizeBranchName("v1.0.0")).toBe("v1.0.0"); + }); + + it("should replace invalid characters with dashes", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + expect(normalizeBranchName("feature@test")).toBe("feature-test"); + expect(normalizeBranchName("branch#with#hashes")).toBe("branch-with-hashes"); + expect(normalizeBranchName("test branch name")).toBe("test-branch-name"); + }); + + it("should collapse multiple dashes", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + expect(normalizeBranchName("test---branch")).toBe("test-branch"); + expect(normalizeBranchName("a--b--c")).toBe("a-b-c"); + }); + + it("should remove leading and trailing dashes", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + expect(normalizeBranchName("-test-branch-")).toBe("test-branch"); + expect(normalizeBranchName("---test---")).toBe("test"); + }); + + it("should truncate to 128 characters", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + const longName = "a".repeat(150); + const result = normalizeBranchName(longName); + expect(result.length).toBe(128); + expect(result).toBe("a".repeat(128)); + }); + + it("should convert to lowercase", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + expect(normalizeBranchName("Feature/Add-Login")).toBe("feature/add-login"); + expect(normalizeBranchName("MY-BRANCH")).toBe("my-branch"); + }); + + it("should handle empty and invalid inputs", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + expect(normalizeBranchName("")).toBe(""); + expect(normalizeBranchName(" ")).toBe(" "); + expect(normalizeBranchName(null)).toBe(null); + expect(normalizeBranchName(undefined)).toBe(undefined); + }); + + it("should preserve valid special characters", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + expect(normalizeBranchName("feature/test_branch-v1.0")).toBe("feature/test_branch-v1.0"); + expect(normalizeBranchName("my_branch-123")).toBe("my_branch-123"); + }); + + it("should handle complex combinations", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + expect(normalizeBranchName("Feature@Test/Branch#123")).toBe("feature-test/branch-123"); + expect(normalizeBranchName("__test__branch__")).toBe("__test__branch__"); + }); + + it("should remove trailing dashes after truncation", async () => { + const { normalizeBranchName } = await import("./normalize_branch_name.cjs"); + + // Create a string that will end with a dash after truncation + const longName = "a".repeat(127) + "-b"; + const result = normalizeBranchName(longName); + expect(result.length).toBeLessThanOrEqual(128); + expect(result).not.toMatch(/-$/); + }); +}); diff --git a/pkg/workflow/js/package.json b/pkg/workflow/js/package.json index b6869c8bc3c..cad6203fa4f 100644 --- a/pkg/workflow/js/package.json +++ b/pkg/workflow/js/package.json @@ -17,7 +17,7 @@ }, "scripts": { "typecheck": "tsc --noEmit", - "test": "npm run typecheck && vitest run", + "test": "npm run typecheck && vitest run --no-file-parallelism", "test:js": "vitest run", "test:js-watch": "vitest", "test:js-coverage": "vitest run --coverage", diff --git a/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs b/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs index 39807f331fd..0eab1fbebf1 100644 --- a/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs @@ -4,7 +4,7 @@ import path from "path"; import { spawn } from "child_process"; import crypto from "crypto"; -describe("safe_outputs_mcp_server.cjs large content handling", () => { +describe.sequential("safe_outputs_mcp_server.cjs large content handling", () => { let originalEnv; let tempOutputDir; let tempConfigFile; @@ -417,16 +417,16 @@ describe("safe_outputs_mcp_server.cjs large content handling", () => { const serverPath = path.join(__dirname, "safe_outputs_mcp_server.cjs"); return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - child.kill(); - reject(new Error("Test timeout")); - }, 10000); - const child = spawn("node", [serverPath], { stdio: ["pipe", "pipe", "pipe"], env: { ...process.env }, }); + const timeout = setTimeout(() => { + child.kill(); + reject(new Error("Test timeout")); + }, 10000); + let stderr = ""; let stdout = ""; diff --git a/pkg/workflow/js/safe_outputs_mcp_server.cjs b/pkg/workflow/js/safe_outputs_mcp_server.cjs index 2263a94fc3e..a8a0f27291f 100644 --- a/pkg/workflow/js/safe_outputs_mcp_server.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_server.cjs @@ -6,58 +6,18 @@ const path = require("path"); const crypto = require("crypto"); const { execSync } = require("child_process"); +const { normalizeBranchName } = require("./normalize_branch_name.cjs"); +const { estimateTokens } = require("./estimate_tokens.cjs"); +const { generateCompactSchema } = require("./generate_compact_schema.cjs"); +const { writeLargeContentToFile } = require("./write_large_content_to_file.cjs"); +const { getCurrentBranch } = require("./get_current_branch.cjs"); +const { getBaseBranch } = require("./get_base_branch.cjs"); +const { generateGitPatch } = require("./generate_git_patch.cjs"); + const encoder = new TextEncoder(); const SERVER_INFO = { name: "safeoutputs", version: "1.0.0" }; const debug = msg => process.stderr.write(`[${SERVER_INFO.name}] ${msg}\n`); -/** - * Normalizes a branch name to be a valid git branch name. - * - * IMPORTANT: Keep this function in sync with the normalizeBranchName function in upload_assets.cjs - * - * Valid characters: alphanumeric (a-z, A-Z, 0-9), dash (-), underscore (_), forward slash (/), dot (.) - * Max length: 128 characters - * - * The normalization process: - * 1. Replaces invalid characters with a single dash - * 2. Collapses multiple consecutive dashes to a single dash - * 3. Removes leading and trailing dashes - * 4. Truncates to 128 characters - * 5. Removes trailing dashes after truncation - * 6. Converts to lowercase - * - * @param {string} branchName - The branch name to normalize - * @returns {string} The normalized branch name - */ -function normalizeBranchName(branchName) { - if (!branchName || typeof branchName !== "string" || branchName.trim() === "") { - return branchName; - } - - // Replace any sequence of invalid characters with a single dash - // Valid characters are: a-z, A-Z, 0-9, -, _, /, . - let normalized = branchName.replace(/[^a-zA-Z0-9\-_/.]+/g, "-"); - - // Collapse multiple consecutive dashes to a single dash - normalized = normalized.replace(/-+/g, "-"); - - // Remove leading and trailing dashes - normalized = normalized.replace(/^-+|-+$/g, ""); - - // Truncate to max 128 characters - if (normalized.length > 128) { - normalized = normalized.substring(0, 128); - } - - // Ensure it doesn't end with a dash after truncation - normalized = normalized.replace(/-+$/, ""); - - // Convert to lowercase - normalized = normalized.toLowerCase(); - - return normalized; -} - // Read configuration from file const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json"; let safeOutputsConfigRaw; @@ -179,87 +139,6 @@ function replyError(id, code, message) { writeMessage(res); } -/** - * Estimates token count from text using 4 chars per token estimate - * @param {string} text - The text to estimate tokens for - * @returns {number} Approximate token count - */ -function estimateTokens(text) { - if (!text) return 0; - return Math.ceil(text.length / 4); -} - -/** - * Generates a compact schema description from JSON content - * @param {string} content - The JSON content to analyze - * @returns {string} Compact schema description for jq/agent - */ -function generateCompactSchema(content) { - try { - const parsed = JSON.parse(content); - - // Generate a compact schema based on the structure - if (Array.isArray(parsed)) { - if (parsed.length === 0) { - return "[]"; - } - // For arrays, describe the first element's structure - const firstItem = parsed[0]; - if (typeof firstItem === "object" && firstItem !== null) { - const keys = Object.keys(firstItem); - return `[{${keys.join(", ")}}] (${parsed.length} items)`; - } - return `[${typeof firstItem}] (${parsed.length} items)`; - } else if (typeof parsed === "object" && parsed !== null) { - // For objects, list top-level keys - const keys = Object.keys(parsed); - if (keys.length > 10) { - return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; - } - return `{${keys.join(", ")}}`; - } - - return `${typeof parsed}`; - } catch { - // If not valid JSON, return generic description - return "text content"; - } -} - -/** - * Writes large content to a file and returns metadata - * @param {string} content - The content to write - * @returns {Object} Object with filename and description - */ -function writeLargeContentToFile(content) { - const logsDir = "/tmp/gh-aw/safeoutputs"; - - // Ensure directory exists - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - - // Generate SHA256 hash of content - const hash = crypto.createHash("sha256").update(content).digest("hex"); - - // MCP tools return JSON, so always use .json extension - const filename = `${hash}.json`; - const filepath = path.join(logsDir, filename); - - // Write content to file - fs.writeFileSync(filepath, content, "utf8"); - - debug(`Wrote large content (${content.length} chars) to ${filepath}`); - - // Generate compact schema description for jq/agent - const description = generateCompactSchema(content); - - return { - filename: filename, - description: description, - }; -} - function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); // Normalize type to use underscores (convert any dashes to underscores) @@ -428,217 +307,6 @@ const uploadAssetHandler = args => { }; }; -/** - * Get the current git branch name - * @returns {string} The current branch name - */ -function getCurrentBranch() { - // Priority 1: Try git command first to get the actual checked-out branch - // This is more reliable than environment variables which may not reflect - // branch changes made during the workflow execution - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - try { - const branch = execSync("git rev-parse --abbrev-ref HEAD", { - encoding: "utf8", - cwd: cwd, - }).trim(); - debug(`Resolved current branch from git in ${cwd}: ${branch}`); - return branch; - } catch (error) { - debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`); - } - - // Priority 2: Fallback to GitHub Actions environment variables - // GITHUB_HEAD_REF is set for pull_request events and contains the source branch name - // GITHUB_REF_NAME is set for all events and contains the branch/tag name - const ghHeadRef = process.env.GITHUB_HEAD_REF; - const ghRefName = process.env.GITHUB_REF_NAME; - - if (ghHeadRef) { - debug(`Resolved current branch from GITHUB_HEAD_REF: ${ghHeadRef}`); - return ghHeadRef; - } - - if (ghRefName) { - debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`); - return ghRefName; - } - - throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available"); -} - -/** - * Get the base branch name from environment variable - * @returns {string} The base branch name (defaults to "main") - */ -function getBaseBranch() { - return process.env.GH_AW_BASE_BRANCH || "main"; -} - -/** - * Generates a git patch file for the current changes - * @param {string} branchName - The branch name to generate patch for - * @returns {Object} Object with patch info or error - */ -function generateGitPatch(branchName) { - const patchPath = "/tmp/gh-aw/aw.patch"; - const cwd = process.env.GITHUB_WORKSPACE || process.cwd(); - const defaultBranch = process.env.DEFAULT_BRANCH || getBaseBranch(); - const githubSha = process.env.GITHUB_SHA; - - debug(`Generating git patch for branch: ${branchName}`); - debug(`Working directory: ${cwd}`); - debug(`Default branch: ${defaultBranch}`); - debug(`GITHUB_SHA: ${githubSha}`); - - // Ensure /tmp/gh-aw directory exists - const patchDir = path.dirname(patchPath); - if (!fs.existsSync(patchDir)) { - fs.mkdirSync(patchDir, { recursive: true }); - } - - let patchGenerated = false; - let errorMessage = null; - - try { - // Strategy 1: If we have a branch name, check if that branch exists and get its diff - if (branchName) { - debug(`Strategy 1: Using named branch: ${branchName}`); - - // Check if the branch exists locally - try { - execSync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd, encoding: "utf8" }); - debug(`Branch ${branchName} exists locally`); - - // Determine base ref for patch generation - let baseRef; - try { - // Check if origin/branchName exists - execSync(`git show-ref --verify --quiet refs/remotes/origin/${branchName}`, { cwd, encoding: "utf8" }); - baseRef = `origin/${branchName}`; - debug(`Using origin/${branchName} as base for patch generation`); - } catch { - // Use merge-base with default branch - debug(`origin/${branchName} does not exist, using merge-base with default branch`); - execSync(`git fetch origin ${defaultBranch}`, { cwd, encoding: "utf8" }); - baseRef = execSync(`git merge-base origin/${defaultBranch} ${branchName}`, { cwd, encoding: "utf8" }).trim(); - debug(`Using merge-base as base: ${baseRef}`); - } - - // Count commits to be included - const commitCount = parseInt(execSync(`git rev-list --count ${baseRef}..${branchName}`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits in patch: ${commitCount}`); - - if (commitCount > 0) { - // Generate patch from the determined base to the branch - const patchContent = execSync(`git format-patch ${baseRef}..${branchName} --stdout`, { - cwd, - encoding: "utf8", - }); - - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from branch: ${branchName} (base: ${baseRef})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between ${baseRef} and ${branchName}`); - } - } catch (branchError) { - debug(`Branch ${branchName} does not exist locally: ${branchError instanceof Error ? branchError.message : String(branchError)}`); - } - } - - // Strategy 2: Check if commits were made to current HEAD since checkout - if (!patchGenerated) { - debug(`Strategy 2: Checking for commits on current HEAD`); - - const currentHead = execSync("git rev-parse HEAD", { cwd, encoding: "utf8" }).trim(); - debug(`Current HEAD: ${currentHead}`); - debug(`Checkout SHA (GITHUB_SHA): ${githubSha}`); - - if (!githubSha) { - errorMessage = "GITHUB_SHA environment variable is not set"; - debug(`ERROR: ${errorMessage}`); - } else if (currentHead === githubSha) { - debug("No commits have been made since checkout (HEAD == GITHUB_SHA)"); - } else { - // Check if GITHUB_SHA is an ancestor of current HEAD - try { - execSync(`git merge-base --is-ancestor ${githubSha} HEAD`, { cwd, encoding: "utf8" }); - debug("GITHUB_SHA is an ancestor of HEAD - commits were added"); - - // Count commits between GITHUB_SHA and HEAD - const commitCount = parseInt(execSync(`git rev-list --count ${githubSha}..HEAD`, { cwd, encoding: "utf8" }).trim(), 10); - debug(`Number of commits added since checkout: ${commitCount}`); - - if (commitCount > 0) { - // Generate patch from GITHUB_SHA to HEAD - const patchContent = execSync(`git format-patch ${githubSha}..HEAD --stdout`, { - cwd, - encoding: "utf8", - }); - - if (patchContent && patchContent.trim()) { - fs.writeFileSync(patchPath, patchContent, "utf8"); - debug(`Patch file created from commits on HEAD (base: ${githubSha})`); - patchGenerated = true; - } else { - debug(`No patch content generated (empty diff)`); - } - } else { - debug(`No commits found between GITHUB_SHA and HEAD`); - } - } catch { - debug("GITHUB_SHA is not an ancestor of HEAD - repository state has diverged"); - } - } - } - } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; - debug(`ERROR: ${errorMessage}`); - } - - // Check if patch was generated and has content - if (patchGenerated && fs.existsSync(patchPath)) { - const patchContent = fs.readFileSync(patchPath, "utf8"); - const patchSize = Buffer.byteLength(patchContent, "utf8"); - const patchLines = patchContent.split("\n").length; - - if (!patchContent.trim()) { - // Empty patch - debug("Patch file is empty - no changes to commit"); - return { - success: false, - error: "No changes to commit - patch is empty", - patchPath: patchPath, - patchSize: 0, - patchLines: 0, - }; - } - - debug(`Patch file created: ${patchPath}`); - debug(`Patch size: ${patchSize} bytes`); - debug(`Patch lines: ${patchLines}`); - - return { - success: true, - patchPath: patchPath, - patchSize: patchSize, - patchLines: patchLines, - }; - } - - // No patch generated - return { - success: false, - error: errorMessage || "No changes to commit - no commits found", - patchPath: patchPath, - }; -} - /** * Handler for create_pull_request tool * Resolves the current branch if branch is not provided or is the base branch diff --git a/pkg/workflow/js/safe_outputs_mcp_server_defaults.test.cjs b/pkg/workflow/js/safe_outputs_mcp_server_defaults.test.cjs index 96478a22f20..3cb983ca8af 100644 --- a/pkg/workflow/js/safe_outputs_mcp_server_defaults.test.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_server_defaults.test.cjs @@ -4,7 +4,7 @@ import path from "path"; import { spawn } from "child_process"; // Test defaults for safe outputs MCP server -describe("safe_outputs_mcp_server.cjs defaults handling", () => { +describe.sequential("safe_outputs_mcp_server.cjs defaults handling", () => { let originalEnv; let tempConfigFile; let tempOutputDir; @@ -348,7 +348,7 @@ describe("safe_outputs_mcp_server.cjs defaults handling", () => { }); // Test that add_labels tool description is patched with allowed labels -describe("safe_outputs_mcp_server.cjs add_labels tool patching", () => { +describe.sequential("safe_outputs_mcp_server.cjs add_labels tool patching", () => { it("should patch add_labels tool description with allowed labels from config", async () => { const config = { add_labels: { @@ -568,7 +568,7 @@ describe("safe_outputs_mcp_server.cjs add_labels tool patching", () => { }); // Test that update_issue tool description is patched with allowed operations -describe("safe_outputs_mcp_server.cjs update_issue tool patching", () => { +describe.sequential("safe_outputs_mcp_server.cjs update_issue tool patching", () => { it("should patch update_issue tool description with allowed operations when some are restricted", async () => { const config = { update_issue: { @@ -793,11 +793,6 @@ describe("safe_outputs_mcp_server.cjs update_issue tool patching", () => { const serverPath = path.join(__dirname, "safe_outputs_mcp_server.cjs"); return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - child.kill(); - reject(new Error("Test timeout")); - }, 5000); - const child = spawn("node", [serverPath], { stdio: ["pipe", "pipe", "pipe"], env: { @@ -807,6 +802,11 @@ describe("safe_outputs_mcp_server.cjs update_issue tool patching", () => { }, }); + const timeout = setTimeout(() => { + child.kill(); + reject(new Error("Test timeout")); + }, 5000); + let receivedMessages = []; child.stdout.on("data", data => { @@ -883,7 +883,7 @@ describe("safe_outputs_mcp_server.cjs update_issue tool patching", () => { }); // Test that upload_asset tool description is patched with constraints from environment -describe("safe_outputs_mcp_server.cjs upload_asset tool patching", () => { +describe.sequential("safe_outputs_mcp_server.cjs upload_asset tool patching", () => { it("should patch upload_asset tool description with max size and allowed extensions", async () => { const config = { upload_asset: {}, @@ -1101,7 +1101,7 @@ describe("safe_outputs_mcp_server.cjs upload_asset tool patching", () => { }); // Test that create_pull_request and push_to_pull_request_branch tools have optional branch parameter -describe("safe_outputs_mcp_server.cjs branch parameter handling", () => { +describe.sequential("safe_outputs_mcp_server.cjs branch parameter handling", () => { it("should have optional branch parameter for create_pull_request", async () => { const config = { create_pull_request: {}, @@ -1314,7 +1314,7 @@ describe("safe_outputs_mcp_server.cjs branch parameter handling", () => { }); // Test that tool call responses include the isError field -describe("safe_outputs_mcp_server.cjs tool call response format", () => { +describe.sequential("safe_outputs_mcp_server.cjs tool call response format", () => { it("should include isError field in tool call responses", async () => { const config = { create_issue: {}, diff --git a/pkg/workflow/js/write_large_content_to_file.cjs b/pkg/workflow/js/write_large_content_to_file.cjs new file mode 100644 index 00000000000..89561cf2763 --- /dev/null +++ b/pkg/workflow/js/write_large_content_to_file.cjs @@ -0,0 +1,44 @@ +// @ts-check +/// + +const fs = require("fs"); +const path = require("path"); +const crypto = require("crypto"); + +const { generateCompactSchema } = require("./generate_compact_schema.cjs"); + +/** + * Writes large content to a file and returns metadata + * @param {string} content - The content to write + * @returns {Object} Object with filename and description + */ +function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safeoutputs"; + + // Ensure directory exists + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + + // Generate SHA256 hash of content + const hash = crypto.createHash("sha256").update(content).digest("hex"); + + // MCP tools return JSON, so always use .json extension + const filename = `${hash}.json`; + const filepath = path.join(logsDir, filename); + + // Write content to file + fs.writeFileSync(filepath, content, "utf8"); + + // Generate compact schema description for jq/agent + const description = generateCompactSchema(content); + + return { + filename: filename, + description: description, + }; +} + +module.exports = { + writeLargeContentToFile, +}; diff --git a/pkg/workflow/js/write_large_content_to_file.test.cjs b/pkg/workflow/js/write_large_content_to_file.test.cjs new file mode 100644 index 00000000000..02ed10601a7 --- /dev/null +++ b/pkg/workflow/js/write_large_content_to_file.test.cjs @@ -0,0 +1,117 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import fs from "fs"; +import path from "path"; + +describe("writeLargeContentToFile", () => { + const testDir = "/tmp/gh-aw/safeoutputs"; + + beforeEach(() => { + // Clean up test directory before each test + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true }); + } + }); + + afterEach(() => { + // Clean up test directory after each test + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true }); + } + }); + + it("should create directory if it doesn't exist", async () => { + const { writeLargeContentToFile } = await import("./write_large_content_to_file.cjs"); + + expect(fs.existsSync(testDir)).toBe(false); + + const content = JSON.stringify({ test: "data" }); + writeLargeContentToFile(content); + + expect(fs.existsSync(testDir)).toBe(true); + }); + + it("should write content to file with hash-based filename", async () => { + const { writeLargeContentToFile } = await import("./write_large_content_to_file.cjs"); + + const content = JSON.stringify({ test: "data" }); + const result = writeLargeContentToFile(content); + + expect(result).toHaveProperty("filename"); + expect(result.filename).toMatch(/^[a-f0-9]{64}\.json$/); + + const filepath = path.join(testDir, result.filename); + expect(fs.existsSync(filepath)).toBe(true); + + const written = fs.readFileSync(filepath, "utf8"); + expect(written).toBe(content); + }); + + it("should return schema description", async () => { + const { writeLargeContentToFile } = await import("./write_large_content_to_file.cjs"); + + const content = JSON.stringify({ id: 1, name: "test", value: 10 }); + const result = writeLargeContentToFile(content); + + expect(result).toHaveProperty("description"); + expect(result.description).toBe("{id, name, value}"); + }); + + it("should use .json extension", async () => { + const { writeLargeContentToFile } = await import("./write_large_content_to_file.cjs"); + + const content = JSON.stringify([1, 2, 3]); + const result = writeLargeContentToFile(content); + + expect(result.filename).toMatch(/\.json$/); + }); + + it("should generate consistent hash for same content", async () => { + const { writeLargeContentToFile } = await import("./write_large_content_to_file.cjs"); + + const content = JSON.stringify({ test: "data" }); + const result1 = writeLargeContentToFile(content); + const result2 = writeLargeContentToFile(content); + + expect(result1.filename).toBe(result2.filename); + }); + + it("should generate different hash for different content", async () => { + const { writeLargeContentToFile } = await import("./write_large_content_to_file.cjs"); + + const content1 = JSON.stringify({ test: "data1" }); + const content2 = JSON.stringify({ test: "data2" }); + + const result1 = writeLargeContentToFile(content1); + const result2 = writeLargeContentToFile(content2); + + expect(result1.filename).not.toBe(result2.filename); + }); + + it("should handle arrays", async () => { + const { writeLargeContentToFile } = await import("./write_large_content_to_file.cjs"); + + const content = JSON.stringify([{ id: 1 }, { id: 2 }]); + const result = writeLargeContentToFile(content); + + expect(result.description).toBe("[{id}] (2 items)"); + }); + + it("should handle large content", async () => { + const { writeLargeContentToFile } = await import("./write_large_content_to_file.cjs"); + + const largeObj = {}; + for (let i = 0; i < 1000; i++) { + largeObj[`key${i}`] = `value${i}`; + } + const content = JSON.stringify(largeObj); + + const result = writeLargeContentToFile(content); + + expect(result).toHaveProperty("filename"); + expect(result).toHaveProperty("description"); + + const filepath = path.join(testDir, result.filename); + const written = fs.readFileSync(filepath, "utf8"); + expect(written).toBe(content); + }); +}); diff --git a/pkg/workflow/mcp_servers.go b/pkg/workflow/mcp_servers.go index 4100826a457..4f64afeef95 100644 --- a/pkg/workflow/mcp_servers.go +++ b/pkg/workflow/mcp_servers.go @@ -141,7 +141,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, yaml.WriteString(" cat > /tmp/gh-aw/safeoutputs/mcp-server.cjs << 'EOF'\n") // Embed the safe-outputs MCP server script - for _, line := range FormatJavaScriptForYAML(safeOutputsMCPServerScript) { + for _, line := range FormatJavaScriptForYAML(GetSafeOutputsMCPServerScript()) { yaml.WriteString(line) } yaml.WriteString(" EOF\n")